From 7115f60b91b5717d90f643fd692010905c7004db Mon Sep 17 00:00:00 2001 From: Bryan Galdrikian Date: Thu, 31 May 2018 11:36:08 -0700 Subject: Blast 1.1.3. See docs/release_notes.txt. --- test/src/BlastBaseTest.h | 276 +-- test/src/TkBaseTest.h | 970 +++++----- test/src/perf/BlastBasePerfTest.h | 788 ++++---- test/src/perf/DamagePerfTests.cpp | 572 +++--- test/src/perf/SolverPerfTests.cpp | 404 ++-- test/src/unit/APITests.cpp | 3446 ++++++++++++++++----------------- test/src/unit/ActorTests.cpp | 2236 ++++++++++----------- test/src/unit/AssetTests.cpp | 1716 ++++++++-------- test/src/unit/AuthoringCutout.cpp | 410 ---- test/src/unit/CoreTests.cpp | 866 ++++----- test/src/unit/FamilyGraphTests.cpp | 810 ++++---- test/src/unit/MultithreadingTests.cpp | 840 ++++---- test/src/unit/SyncTests.cpp | 688 +++---- test/src/unit/TkCompositeTests.cpp | 1550 +++++++-------- test/src/unit/TkTests.cpp | 3214 +++++++++++++++--------------- test/src/utils/TaskDispatcher.h | 438 ++--- test/src/utils/TestAssets.cpp | 838 ++++---- test/src/utils/TestAssets.h | 140 +- test/src/utils/TestProfiler.h | 70 +- 19 files changed, 9931 insertions(+), 10341 deletions(-) mode change 100644 => 100755 test/src/BlastBaseTest.h mode change 100644 => 100755 test/src/TkBaseTest.h mode change 100644 => 100755 test/src/perf/BlastBasePerfTest.h mode change 100644 => 100755 test/src/perf/DamagePerfTests.cpp mode change 100644 => 100755 test/src/perf/SolverPerfTests.cpp mode change 100644 => 100755 test/src/unit/APITests.cpp mode change 100644 => 100755 test/src/unit/ActorTests.cpp mode change 100644 => 100755 test/src/unit/AssetTests.cpp delete mode 100644 test/src/unit/AuthoringCutout.cpp mode change 100644 => 100755 test/src/unit/CoreTests.cpp mode change 100644 => 100755 test/src/unit/FamilyGraphTests.cpp mode change 100644 => 100755 test/src/unit/MultithreadingTests.cpp mode change 100644 => 100755 test/src/unit/SyncTests.cpp mode change 100644 => 100755 test/src/unit/TkCompositeTests.cpp mode change 100644 => 100755 test/src/unit/TkTests.cpp mode change 100644 => 100755 test/src/utils/TaskDispatcher.h mode change 100644 => 100755 test/src/utils/TestAssets.cpp mode change 100644 => 100755 test/src/utils/TestAssets.h mode change 100644 => 100755 test/src/utils/TestProfiler.h (limited to 'test/src') diff --git a/test/src/BlastBaseTest.h b/test/src/BlastBaseTest.h old mode 100644 new mode 100755 index c570327..9fb7c69 --- a/test/src/BlastBaseTest.h +++ b/test/src/BlastBaseTest.h @@ -1,138 +1,138 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#ifndef BLASTBASETEST_H -#define BLASTBASETEST_H - - -#include "NvBlastTkFramework.h" - -#include "gtest/gtest.h" - -#include "NvBlast.h" - -#include "TestAssets.h" - -#include "NvBlastGlobals.h" - -#include - - -template -class BlastBaseTest : public testing::Test, public Nv::Blast::ErrorCallback -{ -public: - BlastBaseTest() - { - NvBlastGlobalSetErrorCallback(this); - } - - // A zeroing alloc with the same signature as malloc - static void* alignedZeroedAlloc(size_t size) - { - return memset(NVBLAST_ALLOC(size), 0, size); - } - - static void alignedFree(void* mem) - { - NVBLAST_FREE(mem); - } - - // Message log for blast functions - static void messageLog(int type, const char* msg, const char* file, int line) - { - if (FailLevel >= type) - { - switch (type) - { - case NvBlastMessage::Error: EXPECT_TRUE(false) << "NvBlast Error message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Warning: EXPECT_TRUE(false) << "NvBlast Warning message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Info: EXPECT_TRUE(false) << "NvBlast Info message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Debug: EXPECT_TRUE(false) << "NvBlast Debug message in " << file << "(" << line << "): " << msg << "\n"; break; - } - } - else - if (Verbosity > 0) - { - switch (type) - { - case NvBlastMessage::Error: std::cout << "NvBlast Error message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Warning: std::cout << "NvBlast Warning message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Info: std::cout << "NvBlast Info message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Debug: std::cout << "NvBlast Debug message in " << file << "(" << line << "): " << msg << "\n"; break; - } - } - } - - // ErrorCallback interface - virtual void reportError(Nv::Blast::ErrorCode::Enum code, const char* message, const char* file, int line) override - { - uint32_t failMask = 0; - switch (FailLevel) - { - case NvBlastMessage::Debug: - case NvBlastMessage::Info: failMask |= Nv::Blast::ErrorCode::eDEBUG_INFO; - case NvBlastMessage::Warning: failMask |= Nv::Blast::ErrorCode::eDEBUG_WARNING; - case NvBlastMessage::Error: failMask |= Nv::Blast::ErrorCode::eABORT | Nv::Blast::ErrorCode::eABORT | Nv::Blast::ErrorCode::eINTERNAL_ERROR | Nv::Blast::ErrorCode::eOUT_OF_MEMORY | Nv::Blast::ErrorCode::eINVALID_OPERATION | Nv::Blast::ErrorCode::eINVALID_PARAMETER; - default: break; - } - - if (!(failMask & code) && Verbosity <= 0) - { - return; - } - - std::string output = "NvBlast Test "; - switch (code) - { - case Nv::Blast::ErrorCode::eNO_ERROR: break; - case Nv::Blast::ErrorCode::eDEBUG_INFO: output += "Debug Info"; break; - case Nv::Blast::ErrorCode::eDEBUG_WARNING: output += "Debug Warning"; break; - case Nv::Blast::ErrorCode::eINVALID_PARAMETER: output += "Invalid Parameter"; break; - case Nv::Blast::ErrorCode::eINVALID_OPERATION: output += "Invalid Operation"; break; - case Nv::Blast::ErrorCode::eOUT_OF_MEMORY: output += "Out of Memory"; break; - case Nv::Blast::ErrorCode::eINTERNAL_ERROR: output += "Internal Error"; break; - case Nv::Blast::ErrorCode::eABORT: output += "Abort"; break; - case Nv::Blast::ErrorCode::ePERF_WARNING: output += "Perf Warning"; break; - default: FAIL(); - } - output += std::string(" message in ") + file + "(" + std::to_string(line) + "): " + message + "\n"; - - if (failMask & code) - { - EXPECT_TRUE(false) << output; - } - else - { - std::cout << output; - } - } -}; - - -#endif // #ifndef BLASTBASETEST_H +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef BLASTBASETEST_H +#define BLASTBASETEST_H + + +#include "NvBlastTkFramework.h" + +#include "gtest/gtest.h" + +#include "NvBlast.h" + +#include "TestAssets.h" + +#include "NvBlastGlobals.h" + +#include + + +template +class BlastBaseTest : public testing::Test, public Nv::Blast::ErrorCallback +{ +public: + BlastBaseTest() + { + NvBlastGlobalSetErrorCallback(this); + } + + // A zeroing alloc with the same signature as malloc + static void* alignedZeroedAlloc(size_t size) + { + return memset(NVBLAST_ALLOC(size), 0, size); + } + + static void alignedFree(void* mem) + { + NVBLAST_FREE(mem); + } + + // Message log for blast functions + static void messageLog(int type, const char* msg, const char* file, int line) + { + if (FailLevel >= type) + { + switch (type) + { + case NvBlastMessage::Error: EXPECT_TRUE(false) << "NvBlast Error message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Warning: EXPECT_TRUE(false) << "NvBlast Warning message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Info: EXPECT_TRUE(false) << "NvBlast Info message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Debug: EXPECT_TRUE(false) << "NvBlast Debug message in " << file << "(" << line << "): " << msg << "\n"; break; + } + } + else + if (Verbosity > 0) + { + switch (type) + { + case NvBlastMessage::Error: std::cout << "NvBlast Error message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Warning: std::cout << "NvBlast Warning message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Info: std::cout << "NvBlast Info message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Debug: std::cout << "NvBlast Debug message in " << file << "(" << line << "): " << msg << "\n"; break; + } + } + } + + // ErrorCallback interface + virtual void reportError(Nv::Blast::ErrorCode::Enum code, const char* message, const char* file, int line) override + { + uint32_t failMask = 0; + switch (FailLevel) + { + case NvBlastMessage::Debug: + case NvBlastMessage::Info: failMask |= Nv::Blast::ErrorCode::eDEBUG_INFO; + case NvBlastMessage::Warning: failMask |= Nv::Blast::ErrorCode::eDEBUG_WARNING; + case NvBlastMessage::Error: failMask |= Nv::Blast::ErrorCode::eABORT | Nv::Blast::ErrorCode::eABORT | Nv::Blast::ErrorCode::eINTERNAL_ERROR | Nv::Blast::ErrorCode::eOUT_OF_MEMORY | Nv::Blast::ErrorCode::eINVALID_OPERATION | Nv::Blast::ErrorCode::eINVALID_PARAMETER; + default: break; + } + + if (!(failMask & code) && Verbosity <= 0) + { + return; + } + + std::string output = "NvBlast Test "; + switch (code) + { + case Nv::Blast::ErrorCode::eNO_ERROR: break; + case Nv::Blast::ErrorCode::eDEBUG_INFO: output += "Debug Info"; break; + case Nv::Blast::ErrorCode::eDEBUG_WARNING: output += "Debug Warning"; break; + case Nv::Blast::ErrorCode::eINVALID_PARAMETER: output += "Invalid Parameter"; break; + case Nv::Blast::ErrorCode::eINVALID_OPERATION: output += "Invalid Operation"; break; + case Nv::Blast::ErrorCode::eOUT_OF_MEMORY: output += "Out of Memory"; break; + case Nv::Blast::ErrorCode::eINTERNAL_ERROR: output += "Internal Error"; break; + case Nv::Blast::ErrorCode::eABORT: output += "Abort"; break; + case Nv::Blast::ErrorCode::ePERF_WARNING: output += "Perf Warning"; break; + default: FAIL(); + } + output += std::string(" message in ") + file + "(" + std::to_string(line) + "): " + message + "\n"; + + if (failMask & code) + { + EXPECT_TRUE(false) << output; + } + else + { + std::cout << output; + } + } +}; + + +#endif // #ifndef BLASTBASETEST_H diff --git a/test/src/TkBaseTest.h b/test/src/TkBaseTest.h old mode 100644 new mode 100755 index f732f8d..3ffcf6c --- a/test/src/TkBaseTest.h +++ b/test/src/TkBaseTest.h @@ -1,485 +1,485 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#ifndef TKBASETEST_H -#define TKBASETEST_H - - -#include "NvBlastTk.h" -#include "NvBlastTkActor.h" -#include "NvBlastPxCallbacks.h" - -#include "BlastBaseTest.h" - -#include "NvBlastExtDamageShaders.h" - -#include "NvBlastIndexFns.h" -#include "NvBlastExtCustomProfiler.h" -#include "TestProfiler.h" -#include "NvBlastExtPxTask.h" - -#include "PxCpuDispatcher.h" -#include "PxTask.h" -#include "PxFoundation.h" -#include "PxFoundationVersion.h" - -#include -#include -#include -#include -#include -#include - - -#define USE_PHYSX_DISPATCHER 0 - -#if USE_PHYSX_DISPATCHER -#include "PxDefaultCpuDispatcher.h" -#endif - - -using namespace Nv::Blast; -using namespace physx; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Helpers -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -NV_INLINE void ExpectArrayMatch(TkObject** arr0, size_t size0, TkObject** arr1, size_t size1) -{ - EXPECT_TRUE(size0 == size1); - std::set set0(arr0, arr0 + size0); - std::set set1(arr1, arr1 + size1); - EXPECT_TRUE(set0 == set1); -} - -class TestCpuDispatcher : public physx::PxCpuDispatcher -{ - struct SharedContext - { - std::queue workQueue; - std::condition_variable cv; - std::mutex mutex; - std::atomic quit; - }; - - void submitTask(PxBaseTask& task) override - { - if (m_threads.size() > 0) - { - std::unique_lock lk(m_context.mutex); - m_context.workQueue.push(&task); - lk.unlock(); - m_context.cv.notify_one(); - } - else - { - TEST_ZONE_BEGIN(task.getName()); - task.run(); - TEST_ZONE_END(task.getName()); - task.release(); - } - } - - uint32_t getWorkerCount() const override { return (uint32_t)m_threads.size(); } - - static void execute(SharedContext& context) - { - while (!context.quit) - { - std::unique_lock lk(context.mutex); - if (!context.workQueue.empty()) - { - PxBaseTask& task = *context.workQueue.front(); - context.workQueue.pop(); - lk.unlock(); - TEST_ZONE_BEGIN(task.getName()); - task.run(); - TEST_ZONE_END(task.getName()); - task.release(); - } - else - { - // shared variables must be modified under the mutex in order - // to correctly publish the modification to the waiting thread - context.cv.wait(lk, [&]{ return !context.workQueue.empty() || context.quit; }); - } - } - } - - SharedContext m_context; - std::vector m_threads; - -public: - TestCpuDispatcher(uint32_t numWorkers) - { - m_context.quit = false; - for (uint32_t i = 0; i < numWorkers; ++i) - { - m_threads.push_back(std::thread(execute, std::ref(m_context))); - } - } - - void release() - { - std::unique_lock lk(m_context.mutex); - m_context.quit = true; - lk.unlock(); - m_context.cv.notify_all(); - for (std::thread& t : m_threads) - { - t.join(); - } - delete this; - } -}; - - -struct CSParams -{ - CSParams(uint32_t axis_, float coord_) : axis(axis_), coord(coord_) {} - uint32_t axis; - float coord; -}; - -static void CubeSlicer(NvBlastFractureBuffers* outbuf, const NvBlastGraphShaderActor* actor, const void* params) -{ - uint32_t bondFractureCount = 0; - uint32_t bondFractureCountMax = outbuf->bondFractureCount; - - const CSParams& p = *reinterpret_cast (reinterpret_cast(params)->damageDesc); - - uint32_t currentNodeIndex = actor->firstGraphNodeIndex; - while (!Nv::Blast::isInvalidIndex(currentNodeIndex)) - { - for (uint32_t adj = actor->adjacencyPartition[currentNodeIndex]; adj < actor->adjacencyPartition[currentNodeIndex + 1]; ++adj) - { - if (currentNodeIndex < actor->adjacentNodeIndices[adj]) - { - if (actor->assetBonds[actor->adjacentBondIndices[adj]].centroid[p.axis] == p.coord && bondFractureCount < bondFractureCountMax) - { - NvBlastBondFractureData& data = outbuf->bondFractures[bondFractureCount++]; - data.userdata = 0; - data.nodeIndex0 = currentNodeIndex; - data.nodeIndex1 = actor->adjacentNodeIndices[adj]; - data.health = 1.0f; - } - } - } - currentNodeIndex = actor->graphNodeIndexLinks[currentNodeIndex]; - } - - outbuf->bondFractureCount = bondFractureCount; - outbuf->chunkFractureCount = 0; - - //printf("slicer outcount %d\n", bondFractureCount); -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// TkBaseTest Class -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -template -class TkBaseTest : public BlastBaseTest -{ -public: - TkBaseTest() : m_cpuDispatcher(), m_taskman(nullptr), m_foundation(nullptr) - { - } - - virtual void SetUp() override - { - m_foundation = PxCreateFoundation(PX_FOUNDATION_VERSION, NvBlastGetPxAllocatorCallback(), NvBlastGetPxErrorCallback()); - - NvBlastProfilerSetCallback(&m_profiler); - NvBlastProfilerSetDetail(Nv::Blast::ProfilerDetail::LOW); - m_profiler.setPlatformEnabled(true); - -#if USE_PHYSX_DISPATCHER - PxU32 affinity[] = { 1, 2, 4, 8 }; - m_cpuDispatcher = PxDefaultCpuDispatcherCreate(4, affinity); - m_cpuDispatcher->setRunProfiled(false); -#else - m_cpuDispatcher = new TestCpuDispatcher(4); -#endif - - m_taskman = PxTaskManager::createTaskManager(NvBlastGetPxErrorCallback(), m_cpuDispatcher, nullptr); - m_groupTM = ExtGroupTaskManager::create(*m_taskman); - } - - virtual void TearDown() override - { - m_groupTM->release(); - m_cpuDispatcher->release(); - if (m_taskman) m_taskman->release(); - if (m_foundation) m_foundation->release(); - } - - void createFramework() - { - TkFramework* framework = NvBlastTkFrameworkCreate(); - EXPECT_TRUE(framework != nullptr); - EXPECT_EQ(framework, NvBlastTkFrameworkGet()); - } - - void releaseFramework() - { - TkFramework* framework = NvBlastTkFrameworkGet(); - framework->release(); - EXPECT_TRUE(NvBlastTkFrameworkGet() == nullptr); - } - - void createTestAssets(bool addInternalJoints = false) - { - const uint8_t cube1BondDescFlags_internalJoints[12] = - { - TkAssetDesc::NoFlags, - TkAssetDesc::NoFlags, - TkAssetDesc::NoFlags, - TkAssetDesc::NoFlags, - - TkAssetDesc::NoFlags, - TkAssetDesc::NoFlags, - TkAssetDesc::NoFlags, - TkAssetDesc::NoFlags, - - TkAssetDesc::BondJointed, - TkAssetDesc::BondJointed, - TkAssetDesc::BondJointed, - TkAssetDesc::BondJointed - }; - - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - TkFramework* framework = NvBlastTkFrameworkGet(); - for (uint32_t i = 0; i < assetDescCount; ++i) - { - TkAssetDesc desc; - reinterpret_cast(desc) = g_assetDescs[i]; - desc.bondFlags = addInternalJoints ? cube1BondDescFlags_internalJoints : nullptr; - testAssets.push_back(framework->createAsset(desc)); - EXPECT_TRUE(testAssets[i] != nullptr); - } - } - - TkAsset* createCubeAsset(size_t maxDepth, size_t width, int32_t supportDepth = -1, bool addInternalJoints = false) - { - TkFramework* framework = NvBlastTkFrameworkGet(); - GeneratorAsset cube; - TkAssetDesc assetDesc; - generateCube(cube, assetDesc, maxDepth, width, supportDepth); - std::vector bondFlags(assetDesc.bondCount); - std::fill(bondFlags.begin(), bondFlags.end(), addInternalJoints ? 1 : 0); - assetDesc.bondFlags = bondFlags.data(); - TkAsset* cubeAsset = framework->createAsset(assetDesc); - testAssets.push_back(cubeAsset); - return cubeAsset; - } - - void releaseTestAssets() - { - for (uint32_t i = 0; i < testAssets.size(); ++i) - { - testAssets[i]->release(); - } - testAssets.clear(); - } - - NvBlastExtRadialDamageDesc getRadialDamageDesc(float x, float y, float z, float minRadius = 10.0f, float maxRadius = 10.0f, float damage = 1.0f) - { - NvBlastExtRadialDamageDesc desc; - desc.position[0] = x; - desc.position[1] = y; - desc.position[2] = z; - - desc.minRadius = minRadius; - desc.maxRadius = maxRadius; - desc.damage = damage; - return desc; - } - - NvBlastExtShearDamageDesc getShearDamageDesc(float x, float y, float z, float shearX = 1.0f, float shearY = 0.0f, float shearZ = 0.0f, float minRadius = 10.0f, float maxRadius = 10.0f, float damage = 1.0f) - { - NvBlastExtShearDamageDesc desc; - desc.position[0] = x; - desc.position[1] = y; - desc.position[2] = z; - - desc.normal[0] = shearX; - desc.normal[1] = shearY; - desc.normal[2] = shearZ; - - desc.minRadius = minRadius; - desc.maxRadius = maxRadius; - desc.damage = damage; - - return desc; - } - - static const NvBlastDamageProgram& getCubeSlicerProgram() - { - static NvBlastDamageProgram program = { CubeSlicer, nullptr }; - return program; - } - - static const NvBlastDamageProgram& getFalloffProgram() - { - static NvBlastDamageProgram program = { NvBlastExtFalloffGraphShader, NvBlastExtFalloffSubgraphShader }; - return program; - } - - static const NvBlastDamageProgram& getShearProgram() - { - static NvBlastDamageProgram program = { NvBlastExtShearGraphShader, NvBlastExtShearSubgraphShader }; - return program; - } - - static const NvBlastExtMaterial* getDefaultMaterial() - { - static NvBlastExtMaterial material; - return &material; - }; - - TkFamily* familySerialization(TkFamily* family); - - - std::vector testAssets; - -#if USE_PHYSX_DISPATCHER - PxDefaultCpuDispatcher* m_cpuDispatcher; -#else - TestCpuDispatcher* m_cpuDispatcher; -#endif - - PxTaskManager* m_taskman; - PxFoundation* m_foundation; - - ExtGroupTaskManager* m_groupTM; - ExtCustomProfiler m_profiler; -}; - - -#define TkPxErrorMask (PxErrorCode::eINVALID_PARAMETER | PxErrorCode::eINVALID_OPERATION | PxErrorCode::eOUT_OF_MEMORY | PxErrorCode::eINTERNAL_ERROR | PxErrorCode::eABORT) -#define TkPxWarningMask (PxErrorCode::eDEBUG_WARNING | PxErrorCode::ePERF_WARNING) - -typedef TkBaseTest TkTestAllowWarnings; -typedef TkBaseTest TkTestStrict; - - -class TestFamilyTracker : public TkEventListener -{ -public: - TestFamilyTracker() {} - - typedef std::pair Actor; - - virtual void receive(const TkEvent* events, uint32_t eventCount) override - { - TEST_ZONE_BEGIN("TestFamilyTracker"); - for (size_t i = 0; i < eventCount; ++i) - { - const TkEvent& e = events[i]; - switch (e.type) - { - case (TkEvent::Split): - { - const TkSplitEvent* splitEvent = e.getPayload(); - EXPECT_EQ((size_t)1, actors.erase(Actor(splitEvent->parentData.family, splitEvent->parentData.index))); - for (size_t i = 0; i < splitEvent->numChildren; ++i) - { - TkActor* a = splitEvent->children[i]; - EXPECT_TRUE(actors.insert(Actor(&a->getFamily(), a->getIndex())).second); - } - break; - } - case (TkEvent::FractureCommand): - { - const TkFractureCommands* fracEvent = e.getPayload(); - EXPECT_TRUE(!isInvalidIndex(fracEvent->tkActorData.index)); -#if 0 - printf("chunks broken: %d\n", fracEvent->buffers.chunkFractureCount); - printf("bonds broken: %d\n", fracEvent->buffers.bondFractureCount); - for (uint32_t t = 0; t < fracEvent->buffers.bondFractureCount; t++) - { - //printf("%x ", fracEvent->buffers.bondFractures[t].userdata); - } - //printf("\n"); -#endif - break; - } - case (TkEvent::FractureEvent): - { - const TkFractureEvents* fracEvent = e.getPayload(); - EXPECT_TRUE(!isInvalidIndex(fracEvent->tkActorData.index)); - break; - } - case (TkEvent::JointUpdate): - { - const TkJointUpdateEvent* jointEvent = e.getPayload(); - TkJoint* joint = jointEvent->joint; - EXPECT_TRUE(joint != nullptr); - - switch (jointEvent->subtype) - { - case TkJointUpdateEvent::External: - EXPECT_TRUE(joints.end() == joints.find(joint)); // We should not have this joint yet - joints.insert(joint); - break; - case TkJointUpdateEvent::Changed: - break; - case TkJointUpdateEvent::Unreferenced: - EXPECT_EQ(1, joints.erase(joint)); - joint->release(); - break; - } - break; - } - default: - break; - } - } - TEST_ZONE_END("TestFamilyTracker"); - } - - void insertActor(const TkActor* actor) - { - actors.insert(TestFamilyTracker::Actor(&actor->getFamily(), actor->getIndex())); - } - - void eraseActor(const TkActor* actor) - { - actors.erase(TestFamilyTracker::Actor(&actor->getFamily(), actor->getIndex())); - } - - std::set actors; - std::set joints; -}; - - -#endif // #ifndef TKBASETEST_H +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef TKBASETEST_H +#define TKBASETEST_H + + +#include "NvBlastTk.h" +#include "NvBlastTkActor.h" +#include "NvBlastPxCallbacks.h" + +#include "BlastBaseTest.h" + +#include "NvBlastExtDamageShaders.h" + +#include "NvBlastIndexFns.h" +#include "NvBlastExtCustomProfiler.h" +#include "TestProfiler.h" +#include "NvBlastExtPxTask.h" + +#include "PxCpuDispatcher.h" +#include "PxTask.h" +#include "PxFoundation.h" +#include "PxFoundationVersion.h" + +#include +#include +#include +#include +#include +#include + + +#define USE_PHYSX_DISPATCHER 0 + +#if USE_PHYSX_DISPATCHER +#include "PxDefaultCpuDispatcher.h" +#endif + + +using namespace Nv::Blast; +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Helpers +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +NV_INLINE void ExpectArrayMatch(TkObject** arr0, size_t size0, TkObject** arr1, size_t size1) +{ + EXPECT_TRUE(size0 == size1); + std::set set0(arr0, arr0 + size0); + std::set set1(arr1, arr1 + size1); + EXPECT_TRUE(set0 == set1); +} + +class TestCpuDispatcher : public physx::PxCpuDispatcher +{ + struct SharedContext + { + std::queue workQueue; + std::condition_variable cv; + std::mutex mutex; + std::atomic quit; + }; + + void submitTask(PxBaseTask& task) override + { + if (m_threads.size() > 0) + { + std::unique_lock lk(m_context.mutex); + m_context.workQueue.push(&task); + lk.unlock(); + m_context.cv.notify_one(); + } + else + { + TEST_ZONE_BEGIN(task.getName()); + task.run(); + TEST_ZONE_END(task.getName()); + task.release(); + } + } + + uint32_t getWorkerCount() const override { return (uint32_t)m_threads.size(); } + + static void execute(SharedContext& context) + { + while (!context.quit) + { + std::unique_lock lk(context.mutex); + if (!context.workQueue.empty()) + { + PxBaseTask& task = *context.workQueue.front(); + context.workQueue.pop(); + lk.unlock(); + TEST_ZONE_BEGIN(task.getName()); + task.run(); + TEST_ZONE_END(task.getName()); + task.release(); + } + else + { + // shared variables must be modified under the mutex in order + // to correctly publish the modification to the waiting thread + context.cv.wait(lk, [&]{ return !context.workQueue.empty() || context.quit; }); + } + } + } + + SharedContext m_context; + std::vector m_threads; + +public: + TestCpuDispatcher(uint32_t numWorkers) + { + m_context.quit = false; + for (uint32_t i = 0; i < numWorkers; ++i) + { + m_threads.push_back(std::thread(execute, std::ref(m_context))); + } + } + + void release() + { + std::unique_lock lk(m_context.mutex); + m_context.quit = true; + lk.unlock(); + m_context.cv.notify_all(); + for (std::thread& t : m_threads) + { + t.join(); + } + delete this; + } +}; + + +struct CSParams +{ + CSParams(uint32_t axis_, float coord_) : axis(axis_), coord(coord_) {} + uint32_t axis; + float coord; +}; + +static void CubeSlicer(NvBlastFractureBuffers* outbuf, const NvBlastGraphShaderActor* actor, const void* params) +{ + uint32_t bondFractureCount = 0; + uint32_t bondFractureCountMax = outbuf->bondFractureCount; + + const CSParams& p = *reinterpret_cast (reinterpret_cast(params)->damageDesc); + + uint32_t currentNodeIndex = actor->firstGraphNodeIndex; + while (!Nv::Blast::isInvalidIndex(currentNodeIndex)) + { + for (uint32_t adj = actor->adjacencyPartition[currentNodeIndex]; adj < actor->adjacencyPartition[currentNodeIndex + 1]; ++adj) + { + if (currentNodeIndex < actor->adjacentNodeIndices[adj]) + { + if (actor->assetBonds[actor->adjacentBondIndices[adj]].centroid[p.axis] == p.coord && bondFractureCount < bondFractureCountMax) + { + NvBlastBondFractureData& data = outbuf->bondFractures[bondFractureCount++]; + data.userdata = 0; + data.nodeIndex0 = currentNodeIndex; + data.nodeIndex1 = actor->adjacentNodeIndices[adj]; + data.health = 1.0f; + } + } + } + currentNodeIndex = actor->graphNodeIndexLinks[currentNodeIndex]; + } + + outbuf->bondFractureCount = bondFractureCount; + outbuf->chunkFractureCount = 0; + + //printf("slicer outcount %d\n", bondFractureCount); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TkBaseTest Class +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +template +class TkBaseTest : public BlastBaseTest +{ +public: + TkBaseTest() : m_cpuDispatcher(), m_taskman(nullptr), m_foundation(nullptr) + { + } + + virtual void SetUp() override + { + m_foundation = PxCreateFoundation(PX_FOUNDATION_VERSION, NvBlastGetPxAllocatorCallback(), NvBlastGetPxErrorCallback()); + + NvBlastProfilerSetCallback(&m_profiler); + NvBlastProfilerSetDetail(Nv::Blast::ProfilerDetail::LOW); + m_profiler.setPlatformEnabled(true); + +#if USE_PHYSX_DISPATCHER + PxU32 affinity[] = { 1, 2, 4, 8 }; + m_cpuDispatcher = PxDefaultCpuDispatcherCreate(4, affinity); + m_cpuDispatcher->setRunProfiled(false); +#else + m_cpuDispatcher = new TestCpuDispatcher(4); +#endif + + m_taskman = PxTaskManager::createTaskManager(NvBlastGetPxErrorCallback(), m_cpuDispatcher, nullptr); + m_groupTM = ExtGroupTaskManager::create(*m_taskman); + } + + virtual void TearDown() override + { + m_groupTM->release(); + m_cpuDispatcher->release(); + if (m_taskman) m_taskman->release(); + if (m_foundation) m_foundation->release(); + } + + void createFramework() + { + TkFramework* framework = NvBlastTkFrameworkCreate(); + EXPECT_TRUE(framework != nullptr); + EXPECT_EQ(framework, NvBlastTkFrameworkGet()); + } + + void releaseFramework() + { + TkFramework* framework = NvBlastTkFrameworkGet(); + framework->release(); + EXPECT_TRUE(NvBlastTkFrameworkGet() == nullptr); + } + + void createTestAssets(bool addInternalJoints = false) + { + const uint8_t cube1BondDescFlags_internalJoints[12] = + { + TkAssetDesc::NoFlags, + TkAssetDesc::NoFlags, + TkAssetDesc::NoFlags, + TkAssetDesc::NoFlags, + + TkAssetDesc::NoFlags, + TkAssetDesc::NoFlags, + TkAssetDesc::NoFlags, + TkAssetDesc::NoFlags, + + TkAssetDesc::BondJointed, + TkAssetDesc::BondJointed, + TkAssetDesc::BondJointed, + TkAssetDesc::BondJointed + }; + + const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + TkFramework* framework = NvBlastTkFrameworkGet(); + for (uint32_t i = 0; i < assetDescCount; ++i) + { + TkAssetDesc desc; + reinterpret_cast(desc) = g_assetDescs[i]; + desc.bondFlags = addInternalJoints ? cube1BondDescFlags_internalJoints : nullptr; + testAssets.push_back(framework->createAsset(desc)); + EXPECT_TRUE(testAssets[i] != nullptr); + } + } + + TkAsset* createCubeAsset(size_t maxDepth, size_t width, int32_t supportDepth = -1, bool addInternalJoints = false) + { + TkFramework* framework = NvBlastTkFrameworkGet(); + GeneratorAsset cube; + TkAssetDesc assetDesc; + generateCube(cube, assetDesc, maxDepth, width, supportDepth); + std::vector bondFlags(assetDesc.bondCount); + std::fill(bondFlags.begin(), bondFlags.end(), addInternalJoints ? 1 : 0); + assetDesc.bondFlags = bondFlags.data(); + TkAsset* cubeAsset = framework->createAsset(assetDesc); + testAssets.push_back(cubeAsset); + return cubeAsset; + } + + void releaseTestAssets() + { + for (uint32_t i = 0; i < testAssets.size(); ++i) + { + testAssets[i]->release(); + } + testAssets.clear(); + } + + NvBlastExtRadialDamageDesc getRadialDamageDesc(float x, float y, float z, float minRadius = 10.0f, float maxRadius = 10.0f, float damage = 1.0f) + { + NvBlastExtRadialDamageDesc desc; + desc.position[0] = x; + desc.position[1] = y; + desc.position[2] = z; + + desc.minRadius = minRadius; + desc.maxRadius = maxRadius; + desc.damage = damage; + return desc; + } + + NvBlastExtShearDamageDesc getShearDamageDesc(float x, float y, float z, float shearX = 1.0f, float shearY = 0.0f, float shearZ = 0.0f, float minRadius = 10.0f, float maxRadius = 10.0f, float damage = 1.0f) + { + NvBlastExtShearDamageDesc desc; + desc.position[0] = x; + desc.position[1] = y; + desc.position[2] = z; + + desc.normal[0] = shearX; + desc.normal[1] = shearY; + desc.normal[2] = shearZ; + + desc.minRadius = minRadius; + desc.maxRadius = maxRadius; + desc.damage = damage; + + return desc; + } + + static const NvBlastDamageProgram& getCubeSlicerProgram() + { + static NvBlastDamageProgram program = { CubeSlicer, nullptr }; + return program; + } + + static const NvBlastDamageProgram& getFalloffProgram() + { + static NvBlastDamageProgram program = { NvBlastExtFalloffGraphShader, NvBlastExtFalloffSubgraphShader }; + return program; + } + + static const NvBlastDamageProgram& getShearProgram() + { + static NvBlastDamageProgram program = { NvBlastExtShearGraphShader, NvBlastExtShearSubgraphShader }; + return program; + } + + static const NvBlastExtMaterial* getDefaultMaterial() + { + static NvBlastExtMaterial material; + return &material; + }; + + TkFamily* familySerialization(TkFamily* family); + + + std::vector testAssets; + +#if USE_PHYSX_DISPATCHER + PxDefaultCpuDispatcher* m_cpuDispatcher; +#else + TestCpuDispatcher* m_cpuDispatcher; +#endif + + PxTaskManager* m_taskman; + PxFoundation* m_foundation; + + ExtGroupTaskManager* m_groupTM; + ExtCustomProfiler m_profiler; +}; + + +#define TkPxErrorMask (PxErrorCode::eINVALID_PARAMETER | PxErrorCode::eINVALID_OPERATION | PxErrorCode::eOUT_OF_MEMORY | PxErrorCode::eINTERNAL_ERROR | PxErrorCode::eABORT) +#define TkPxWarningMask (PxErrorCode::eDEBUG_WARNING | PxErrorCode::ePERF_WARNING) + +typedef TkBaseTest TkTestAllowWarnings; +typedef TkBaseTest TkTestStrict; + + +class TestFamilyTracker : public TkEventListener +{ +public: + TestFamilyTracker() {} + + typedef std::pair Actor; + + virtual void receive(const TkEvent* events, uint32_t eventCount) override + { + TEST_ZONE_BEGIN("TestFamilyTracker"); + for (size_t i = 0; i < eventCount; ++i) + { + const TkEvent& e = events[i]; + switch (e.type) + { + case (TkEvent::Split): + { + const TkSplitEvent* splitEvent = e.getPayload(); + EXPECT_EQ((size_t)1, actors.erase(Actor(splitEvent->parentData.family, splitEvent->parentData.index))); + for (size_t i = 0; i < splitEvent->numChildren; ++i) + { + TkActor* a = splitEvent->children[i]; + EXPECT_TRUE(actors.insert(Actor(&a->getFamily(), a->getIndex())).second); + } + break; + } + case (TkEvent::FractureCommand): + { + const TkFractureCommands* fracEvent = e.getPayload(); + EXPECT_TRUE(!isInvalidIndex(fracEvent->tkActorData.index)); +#if 0 + printf("chunks broken: %d\n", fracEvent->buffers.chunkFractureCount); + printf("bonds broken: %d\n", fracEvent->buffers.bondFractureCount); + for (uint32_t t = 0; t < fracEvent->buffers.bondFractureCount; t++) + { + //printf("%x ", fracEvent->buffers.bondFractures[t].userdata); + } + //printf("\n"); +#endif + break; + } + case (TkEvent::FractureEvent): + { + const TkFractureEvents* fracEvent = e.getPayload(); + EXPECT_TRUE(!isInvalidIndex(fracEvent->tkActorData.index)); + break; + } + case (TkEvent::JointUpdate): + { + const TkJointUpdateEvent* jointEvent = e.getPayload(); + TkJoint* joint = jointEvent->joint; + EXPECT_TRUE(joint != nullptr); + + switch (jointEvent->subtype) + { + case TkJointUpdateEvent::External: + EXPECT_TRUE(joints.end() == joints.find(joint)); // We should not have this joint yet + joints.insert(joint); + break; + case TkJointUpdateEvent::Changed: + break; + case TkJointUpdateEvent::Unreferenced: + EXPECT_EQ(1, joints.erase(joint)); + joint->release(); + break; + } + break; + } + default: + break; + } + } + TEST_ZONE_END("TestFamilyTracker"); + } + + void insertActor(const TkActor* actor) + { + actors.insert(TestFamilyTracker::Actor(&actor->getFamily(), actor->getIndex())); + } + + void eraseActor(const TkActor* actor) + { + actors.erase(TestFamilyTracker::Actor(&actor->getFamily(), actor->getIndex())); + } + + std::set actors; + std::set joints; +}; + + +#endif // #ifndef TKBASETEST_H diff --git a/test/src/perf/BlastBasePerfTest.h b/test/src/perf/BlastBasePerfTest.h old mode 100644 new mode 100755 index 55285f1..8eff9ba --- a/test/src/perf/BlastBasePerfTest.h +++ b/test/src/perf/BlastBasePerfTest.h @@ -1,394 +1,394 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#ifndef BLASTBASEPERFTEST_H -#define BLASTBASEPERFTEST_H - - -#include "BlastBaseTest.h" -#include - -#include -#include - - -template -class DataCollection -{ -public: - struct Stats - { - double m_mean; - double m_sdev; - double m_min; - double m_max; - - Stats() - { - reset(); - } - - void reset() - { - m_mean = 0.0; - m_sdev = 0.0; - m_min = std::numeric_limits().max(); - m_max = -std::numeric_limits().max(); - } - }; - - struct DataSet - { - std::vector m_data; - Stats m_stats; - - void calculateStats() - { - m_stats.reset(); - if (m_data.size() > 0) - { - if (m_data.size() > 1) // Remove top half of values to eliminate outliers - { - std::sort(m_data.begin(), m_data.end()); - m_data.resize(m_data.size() / 2); - } - for (size_t i = 0; i < m_data.size(); ++i) - { - m_stats.m_mean += m_data[i]; - m_stats.m_min = std::min(m_stats.m_min, (double)m_data[i]); - m_stats.m_max = std::max(m_stats.m_max, (double)m_data[i]); - } - m_stats.m_mean /= m_data.size(); - if (m_data.size() > 1) - { - for (size_t i = 0; i < m_data.size(); ++i) - { - m_stats.m_sdev += pow(m_data[i] - m_stats.m_mean, 2); - } - m_stats.m_sdev = sqrt(m_stats.m_sdev / (m_data.size() - 1)); - } - } - } - }; - - DataSet& getDataSet(const std::string& name) - { - auto entry = m_lookup.find(name); - if (entry != m_lookup.end()) - { - return m_dataSets[entry->second]; - } - m_lookup[name] = m_dataSets.size(); - m_dataSets.push_back(DataSet()); - return m_dataSets.back(); - } - - bool dataSetExists(const std::string& name) const - { - return m_lookup.find(name) != m_lookup.end(); - } - - void calculateStats() - { - for (size_t i = 0; i < m_dataSets.size(); ++i) - { - m_dataSets[i].calculateStats(); - } - } - - void test(DataCollection& calibration, double relativeThreshold = 0.10, double tickThreshold = 100.0) - { - for (auto entry = m_lookup.begin(); entry != m_lookup.end(); ++entry) - { - const std::string& name = entry->first; - DataCollection::DataSet& data = m_dataSets[entry->second]; - data.calculateStats(); - - if (!calibration.dataSetExists(name)) - { - FAIL() << "PerfTest is not calibrated!" << std::endl << "Missing DataSet: " << name << std::endl; - } - const DataCollection::DataSet& cal = calibration.getDataSet(name); - const double calMin = cal.m_stats.m_min; - - if (data.m_stats.m_min > (1.0 + relativeThreshold) * calMin && data.m_stats.m_min - calMin > tickThreshold) - { - std::cout << name << ":" << std::endl; - std::cout << "PERF - : Timing (" << data.m_stats.m_min << ") exceeds recorded min (" << calMin << ") by more than allowed relative threshold (" << relativeThreshold*100 << "%) and absolute threshold (" << tickThreshold << " ticks)." << std::endl; - EXPECT_FALSE(data.m_stats.m_min > (1.0 + relativeThreshold) * calMin && data.m_stats.m_min - calMin > tickThreshold) - << name << ":" << std::endl - << "PERF - : Timing (" << data.m_stats.m_min << ") exceeds recorded min (" << calMin << ") by more than allowed relative threshold (" << relativeThreshold * 100 << "%) and absolute threshold (" << tickThreshold << " ticks)." << std::endl; - } - else - if (data.m_stats.m_min < (1.0 - relativeThreshold) * calMin && data.m_stats.m_min - calMin < -tickThreshold) - { - std::cout << name << ":" << std::endl; - std::cout << "PERF + : Timing (" << data.m_stats.m_min << ") is less than the recorded min (" << calMin << ") by more than the relative threshold (" << relativeThreshold * 100 << "%) and absolute threshold (" << tickThreshold << " ticks)." << std::endl; - } - } - } - - size_t size() const - { - return m_dataSets.size(); - } - - void clear() - { - m_lookup.clear(); - m_dataSets.clear(); - } - - template - friend std::istream& operator >> (std::istream& stream, DataCollection& c); - - template - friend std::ostream& operator << (std::ostream& stream, const DataCollection& c); - -private: - std::map m_lookup; - std::vector< DataSet > m_dataSets; -}; - -template -std::istream& operator >> (std::istream& stream, DataCollection& c) -{ - std::string name; - while (!stream.eof()) - { - std::getline(stream >> std::ws, name); - typename DataCollection::DataSet& dataSet = c.getDataSet(name); - stream >> dataSet.m_stats.m_mean >> dataSet.m_stats.m_sdev >> dataSet.m_stats.m_min >> dataSet.m_stats.m_max >> std::ws; - } - return stream; -} - -template -std::ostream& operator << (std::ostream& stream, const DataCollection& c) -{ - for (auto entry = c.m_lookup.begin(); entry != c.m_lookup.end(); ++entry) - { - const std::string& name = entry->first; - stream << name.c_str() << std::endl; - const typename DataCollection::DataSet& data = c.m_dataSets[entry->second]; - stream << data.m_stats.m_mean << " " << data.m_stats.m_sdev << " " << data.m_stats.m_min << " " << data.m_stats.m_max << std::endl; - } - return stream; -} - - -static const char* getPlatformSuffix() -{ -#if NV_WIN32 - return "win32"; -#elif NV_WIN64 - return "win64"; -#elif NV_XBOXONE - return "xb1"; -#elif NV_PS4 - return "ps4"; -#elif NV_LINUX - #if NV_X64 - return "linux64"; - #else - return "linux32"; - #endif -#else - return "gen"; -#endif -} - -static const char* getPlatformRoot() -{ -#if NV_PS4 - return "/app0/"; -#elif NV_XBOXONE - return "G:/"; -#elif NV_LINUX - return "../../"; -#else - return "../../../"; -#endif -} - -static std::string defaultRelativeDataPath() -{ - const char* dataDir = "test/data/"; - - std::string rootDir = getPlatformRoot(); - return rootDir + dataDir + getPlatformSuffix() + "/"; -} - -class PerfTestEngine -{ -public: - PerfTestEngine(const char* collectionName) : m_calibrate(false) - { - m_filename = defaultRelativeDataPath() + std::string(collectionName) + "_" + getPlatformSuffix() + ".cal"; - - auto argvs = testing::internal::GetArgvs(); - size_t argCount = argvs.size(); - - for (size_t argNum = 0; argNum < argCount; ++argNum) - { - if (argvs[argNum] == "-calibrate") - { - m_calibrate = true; - } - else - if (argvs[argNum] == "-calPath") - { - if (++argNum < argCount) - { - m_filename = argvs[argNum]; - } - } - } - - if (!m_calibrate) - { - std::ifstream in; - in.open(m_filename); - if (in.is_open()) - { - std::string name; - std::getline(in, name); // Eat header - std::getline(in, name); // Eat header (2 lines) - in >> m_dataCalibration; - in.close(); - } - m_calibrate = m_dataCalibration.size() == 0; - } - - if (m_calibrate) - { - std::ofstream out; - out.open(m_filename); - if (out.is_open()) - { - out << "Format: timing name (whole line)" << std::endl << "timing mean s.d. min max" << std::endl; // Header (2 lines) - out.close(); - } - } - - if (m_calibrate) - { - std::cout << "******** Calibration Mode ********\n"; - } - else - { - std::cout << "******** Test Mode ********\n"; - std::cout << "Read calibration data from " << m_filename << std::endl; - } - } - - void endTest() - { - if (m_calibrate) - { - m_dataTempCollection.calculateStats(); - std::ofstream out; - out.open(m_filename, std::ofstream::app); - if (out.is_open()) - { - out << m_dataTempCollection; - out.close(); - std::cout << "Calibration stats written to " << m_filename << std::endl; - } - else - { - std::cout << "Failed to open calibration file " << m_filename << ". Stats not written." << std::endl; - FAIL() << "Failed to open calibration file " << m_filename << ". Stats not written." << std::endl; - } - } - else - { - m_dataTempCollection.test(m_dataCalibration); - } - m_dataTempCollection.clear(); - } - - void reportData(const std::string& name, int64_t data) - { - m_dataTempCollection.getDataSet(name).m_data.push_back(data); - } - -private: - std::string m_filename; - bool m_calibrate; - DataCollection m_dataTempCollection; - DataCollection m_dataCalibration; -}; - - -template -class BlastBasePerfTest : public BlastBaseTest -{ -public: - /** - This function allows to create/destroy and get PerfTestEngine in local static variable (works header only). - It allows to have PeftTestEngine alive through life span of gtest TestCase. - */ - static PerfTestEngine* getEngineDeadOrAlive(bool alive = true) - { - static PerfTestEngine* engine = nullptr; - if (alive && !engine) - { - engine = new PerfTestEngine(::testing::UnitTest::GetInstance()->current_test_case()->name()); - } - else if (!alive && engine) - { - delete engine; - engine = nullptr; - } - return engine; - } - - static void SetUpTestCase() - { - getEngineDeadOrAlive(); - } - - static void TearDownTestCase() - { - getEngineDeadOrAlive(false); - } - - void TearDown() override - { - getEngineDeadOrAlive()->endTest(); - } - - void reportData(const std::string& name, int64_t data) - { - getEngineDeadOrAlive()->reportData(name, data); - } -}; - - -#endif // #ifndef BLASTBASEPERFTEST_H +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef BLASTBASEPERFTEST_H +#define BLASTBASEPERFTEST_H + + +#include "BlastBaseTest.h" +#include + +#include +#include + + +template +class DataCollection +{ +public: + struct Stats + { + double m_mean; + double m_sdev; + double m_min; + double m_max; + + Stats() + { + reset(); + } + + void reset() + { + m_mean = 0.0; + m_sdev = 0.0; + m_min = std::numeric_limits().max(); + m_max = -std::numeric_limits().max(); + } + }; + + struct DataSet + { + std::vector m_data; + Stats m_stats; + + void calculateStats() + { + m_stats.reset(); + if (m_data.size() > 0) + { + if (m_data.size() > 1) // Remove top half of values to eliminate outliers + { + std::sort(m_data.begin(), m_data.end()); + m_data.resize(m_data.size() / 2); + } + for (size_t i = 0; i < m_data.size(); ++i) + { + m_stats.m_mean += m_data[i]; + m_stats.m_min = std::min(m_stats.m_min, (double)m_data[i]); + m_stats.m_max = std::max(m_stats.m_max, (double)m_data[i]); + } + m_stats.m_mean /= m_data.size(); + if (m_data.size() > 1) + { + for (size_t i = 0; i < m_data.size(); ++i) + { + m_stats.m_sdev += pow(m_data[i] - m_stats.m_mean, 2); + } + m_stats.m_sdev = sqrt(m_stats.m_sdev / (m_data.size() - 1)); + } + } + } + }; + + DataSet& getDataSet(const std::string& name) + { + auto entry = m_lookup.find(name); + if (entry != m_lookup.end()) + { + return m_dataSets[entry->second]; + } + m_lookup[name] = m_dataSets.size(); + m_dataSets.push_back(DataSet()); + return m_dataSets.back(); + } + + bool dataSetExists(const std::string& name) const + { + return m_lookup.find(name) != m_lookup.end(); + } + + void calculateStats() + { + for (size_t i = 0; i < m_dataSets.size(); ++i) + { + m_dataSets[i].calculateStats(); + } + } + + void test(DataCollection& calibration, double relativeThreshold = 0.10, double tickThreshold = 100.0) + { + for (auto entry = m_lookup.begin(); entry != m_lookup.end(); ++entry) + { + const std::string& name = entry->first; + DataCollection::DataSet& data = m_dataSets[entry->second]; + data.calculateStats(); + + if (!calibration.dataSetExists(name)) + { + FAIL() << "PerfTest is not calibrated!" << std::endl << "Missing DataSet: " << name << std::endl; + } + const DataCollection::DataSet& cal = calibration.getDataSet(name); + const double calMin = cal.m_stats.m_min; + + if (data.m_stats.m_min > (1.0 + relativeThreshold) * calMin && data.m_stats.m_min - calMin > tickThreshold) + { + std::cout << name << ":" << std::endl; + std::cout << "PERF - : Timing (" << data.m_stats.m_min << ") exceeds recorded min (" << calMin << ") by more than allowed relative threshold (" << relativeThreshold*100 << "%) and absolute threshold (" << tickThreshold << " ticks)." << std::endl; + EXPECT_FALSE(data.m_stats.m_min > (1.0 + relativeThreshold) * calMin && data.m_stats.m_min - calMin > tickThreshold) + << name << ":" << std::endl + << "PERF - : Timing (" << data.m_stats.m_min << ") exceeds recorded min (" << calMin << ") by more than allowed relative threshold (" << relativeThreshold * 100 << "%) and absolute threshold (" << tickThreshold << " ticks)." << std::endl; + } + else + if (data.m_stats.m_min < (1.0 - relativeThreshold) * calMin && data.m_stats.m_min - calMin < -tickThreshold) + { + std::cout << name << ":" << std::endl; + std::cout << "PERF + : Timing (" << data.m_stats.m_min << ") is less than the recorded min (" << calMin << ") by more than the relative threshold (" << relativeThreshold * 100 << "%) and absolute threshold (" << tickThreshold << " ticks)." << std::endl; + } + } + } + + size_t size() const + { + return m_dataSets.size(); + } + + void clear() + { + m_lookup.clear(); + m_dataSets.clear(); + } + + template + friend std::istream& operator >> (std::istream& stream, DataCollection& c); + + template + friend std::ostream& operator << (std::ostream& stream, const DataCollection& c); + +private: + std::map m_lookup; + std::vector< DataSet > m_dataSets; +}; + +template +std::istream& operator >> (std::istream& stream, DataCollection& c) +{ + std::string name; + while (!stream.eof()) + { + std::getline(stream >> std::ws, name); + typename DataCollection::DataSet& dataSet = c.getDataSet(name); + stream >> dataSet.m_stats.m_mean >> dataSet.m_stats.m_sdev >> dataSet.m_stats.m_min >> dataSet.m_stats.m_max >> std::ws; + } + return stream; +} + +template +std::ostream& operator << (std::ostream& stream, const DataCollection& c) +{ + for (auto entry = c.m_lookup.begin(); entry != c.m_lookup.end(); ++entry) + { + const std::string& name = entry->first; + stream << name.c_str() << std::endl; + const typename DataCollection::DataSet& data = c.m_dataSets[entry->second]; + stream << data.m_stats.m_mean << " " << data.m_stats.m_sdev << " " << data.m_stats.m_min << " " << data.m_stats.m_max << std::endl; + } + return stream; +} + + +static const char* getPlatformSuffix() +{ +#if NV_WIN32 + return "win32"; +#elif NV_WIN64 + return "win64"; +#elif NV_XBOXONE + return "xb1"; +#elif NV_PS4 + return "ps4"; +#elif NV_LINUX + #if NV_X64 + return "linux64"; + #else + return "linux32"; + #endif +#else + return "gen"; +#endif +} + +static const char* getPlatformRoot() +{ +#if NV_PS4 + return "/app0/"; +#elif NV_XBOXONE + return "G:/"; +#elif NV_LINUX + return "../../"; +#else + return "../../../"; +#endif +} + +static std::string defaultRelativeDataPath() +{ + const char* dataDir = "test/data/"; + + std::string rootDir = getPlatformRoot(); + return rootDir + dataDir + getPlatformSuffix() + "/"; +} + +class PerfTestEngine +{ +public: + PerfTestEngine(const char* collectionName) : m_calibrate(false) + { + m_filename = defaultRelativeDataPath() + std::string(collectionName) + "_" + getPlatformSuffix() + ".cal"; + + auto argvs = testing::internal::GetArgvs(); + size_t argCount = argvs.size(); + + for (size_t argNum = 0; argNum < argCount; ++argNum) + { + if (argvs[argNum] == "-calibrate") + { + m_calibrate = true; + } + else + if (argvs[argNum] == "-calPath") + { + if (++argNum < argCount) + { + m_filename = argvs[argNum]; + } + } + } + + if (!m_calibrate) + { + std::ifstream in; + in.open(m_filename); + if (in.is_open()) + { + std::string name; + std::getline(in, name); // Eat header + std::getline(in, name); // Eat header (2 lines) + in >> m_dataCalibration; + in.close(); + } + m_calibrate = m_dataCalibration.size() == 0; + } + + if (m_calibrate) + { + std::ofstream out; + out.open(m_filename); + if (out.is_open()) + { + out << "Format: timing name (whole line)" << std::endl << "timing mean s.d. min max" << std::endl; // Header (2 lines) + out.close(); + } + } + + if (m_calibrate) + { + std::cout << "******** Calibration Mode ********\n"; + } + else + { + std::cout << "******** Test Mode ********\n"; + std::cout << "Read calibration data from " << m_filename << std::endl; + } + } + + void endTest() + { + if (m_calibrate) + { + m_dataTempCollection.calculateStats(); + std::ofstream out; + out.open(m_filename, std::ofstream::app); + if (out.is_open()) + { + out << m_dataTempCollection; + out.close(); + std::cout << "Calibration stats written to " << m_filename << std::endl; + } + else + { + std::cout << "Failed to open calibration file " << m_filename << ". Stats not written." << std::endl; + FAIL() << "Failed to open calibration file " << m_filename << ". Stats not written." << std::endl; + } + } + else + { + m_dataTempCollection.test(m_dataCalibration); + } + m_dataTempCollection.clear(); + } + + void reportData(const std::string& name, int64_t data) + { + m_dataTempCollection.getDataSet(name).m_data.push_back(data); + } + +private: + std::string m_filename; + bool m_calibrate; + DataCollection m_dataTempCollection; + DataCollection m_dataCalibration; +}; + + +template +class BlastBasePerfTest : public BlastBaseTest +{ +public: + /** + This function allows to create/destroy and get PerfTestEngine in local static variable (works header only). + It allows to have PeftTestEngine alive through life span of gtest TestCase. + */ + static PerfTestEngine* getEngineDeadOrAlive(bool alive = true) + { + static PerfTestEngine* engine = nullptr; + if (alive && !engine) + { + engine = new PerfTestEngine(::testing::UnitTest::GetInstance()->current_test_case()->name()); + } + else if (!alive && engine) + { + delete engine; + engine = nullptr; + } + return engine; + } + + static void SetUpTestCase() + { + getEngineDeadOrAlive(); + } + + static void TearDownTestCase() + { + getEngineDeadOrAlive(false); + } + + void TearDown() override + { + getEngineDeadOrAlive()->endTest(); + } + + void reportData(const std::string& name, int64_t data) + { + getEngineDeadOrAlive()->reportData(name, data); + } +}; + + +#endif // #ifndef BLASTBASEPERFTEST_H diff --git a/test/src/perf/DamagePerfTests.cpp b/test/src/perf/DamagePerfTests.cpp old mode 100644 new mode 100755 index 86707ec..9c7ab0e --- a/test/src/perf/DamagePerfTests.cpp +++ b/test/src/perf/DamagePerfTests.cpp @@ -1,286 +1,286 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBasePerfTest.h" -#include "NvBlastExtDamageShaders.h" -#include "NvBlastExtSerialization.h" -#include "NvBlastTime.h" -#include "PxVec3.h" -#include "PxBounds3.h" -#include -#include -#include - -using namespace Nv::Blast; -using namespace physx; - -static void blast -( - std::set& actorsToDamage, - GeneratorAsset* testAsset, - NvBlastExtDamageAccelerator* accelerator, - GeneratorAsset::Vec3 localPos, - float minRadius, float maxRadius, - float compressiveDamage, - std::vector& history, - NvBlastTimers& timers) -{ - std::vector chunkEvents; /* num lower-support chunks + bonds */ - std::vector bondEvents; /* num lower-support chunks + bonds */ - chunkEvents.resize(testAsset->solverChunks.size()); - bondEvents.resize(testAsset->solverBonds.size()); - - NvBlastExtRadialDamageDesc damage = { - compressiveDamage, - { localPos.x, localPos.y, localPos.z }, - minRadius, - maxRadius - }; - - NvBlastExtMaterial material; - - NvBlastExtProgramParams programParams = - { - &damage, - &material, - accelerator - }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - nullptr - }; - - std::vector splitScratch; - std::vector newActors(testAsset->solverChunks.size()); - - size_t totalNewActorsCount = 0; - for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) - { - NvBlastActor* actor = *k; - - NvBlastFractureBuffers events = { (uint32_t)bondEvents.size(), (uint32_t)chunkEvents.size(), bondEvents.data(), chunkEvents.data() }; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, &timers); - NvBlastActorApplyFracture(nullptr, actor, &events, nullptr, &timers); - - bool removeActor = false; - - if (events.bondFractureCount + events.chunkFractureCount > 0) - { - history.push_back(events.bondFractureCount + events.chunkFractureCount); - - splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = &newActors[totalNewActorsCount]; - const size_t bufferSize = newActors.size() - totalNewActorsCount; - const size_t newActorsCount = NvBlastActorSplit(&result, actor, (uint32_t)bufferSize, splitScratch.data(), nullptr, &timers); - totalNewActorsCount += newActorsCount; - removeActor = newActorsCount > 0; - } - - if (removeActor) - { - k = actorsToDamage.erase(k); - } - else - { - ++k; - } - } - - for (size_t i = 0; i < totalNewActorsCount; ++i) - { - actorsToDamage.insert(newActors[i]); - } -} - -typedef BlastBasePerfTest BlastBasePerfTestStrict; - - -struct PerfResults -{ - int64_t totalTime; - int64_t createTime; -}; - -class PerfTest : public BlastBasePerfTestStrict -{ -public: - - NvBlastAsset* loadAsset(const char* path, ExtSerialization* ser) - { - std::ifstream infileStream(path, std::ios::binary); - if (!infileStream.is_open()) - { - return nullptr; - } - const std::vector inBuffer((std::istreambuf_iterator(infileStream)), std::istreambuf_iterator()); - infileStream.close(); - - NvBlastAsset* asset = static_cast(ser->deserializeFromBuffer(inBuffer.data(), inBuffer.size())); - - return asset; - } - - PerfResults damageLeafSupportActors(const char* testName, uint32_t assetCount, uint32_t familyCount, uint32_t damageCount, int accelType, std::vector& history) - { - PerfResults results; - results.totalTime = 0; - results.createTime = 0; - - const float compressiveDamage = 1.0f; - const uint32_t minChunkCount = 1000; - const uint32_t maxChunkCount = 100000; - - srand(0); - - for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) - { - GeneratorAsset cube; - NvBlastAssetDesc desc; - generateRandomCube(cube, desc, minChunkCount, maxChunkCount); - - { - std::vector scratch; - physx::PxBounds3 bounds = physx::PxBounds3::empty(); -#if 1 - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); - void* mem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - bounds = physx::PxBounds3::centerExtents(physx::PxVec3(0, 0, 0), physx::PxVec3(cube.extents.x, cube.extents.y, cube.extents.z)); -#else - // load asset - NvBlastAsset* asset = nullptr; - ExtSerialization* ser = NvBlastExtSerializationCreate(); - for (int s = 0; s < 5 && !asset; s++) - { - asset = loadAsset(&"../../../../../test/assets/table.blast"[s * 3], ser); - } - EXPECT_TRUE(asset != nullptr); - ser->release(); - uint32_t bc = NvBlastAssetGetBondCount(asset, messageLog); - const NvBlastBond* bonds = NvBlastAssetGetBonds(asset, messageLog); - for (uint32_t i = 0; i < bc; i++) - { - bounds.include(reinterpret_cast(bonds[i].centroid)); - } -#endif - - Nv::Blast::Time t; - NvBlastExtDamageAccelerator* accelerator = NvBlastExtDamageAcceleratorCreate(asset, accelType); - results.createTime += t.getElapsedTicks(); - - // Generate familes - for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) - { - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = nullptr; - actorDesc.uniformInitialBondHealth = 1.0f; - actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* mem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(mem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - EXPECT_TRUE(family != nullptr); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // Generate damage - std::set actors; - actors.insert(actor); - for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) - { - GeneratorAsset::Vec3 localPos = GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f) * 2; - localPos.x *= bounds.getExtents().x; - localPos.y *= bounds.getExtents().y; - localPos.z *= bounds.getExtents().z; - const float relativeDamageRadius = (float)rand() / RAND_MAX * bounds.getExtents().maxElement(); - - NvBlastTimers timers; - NvBlastTimersReset(&timers); - blast(actors, &cube, accelerator, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage, history, timers); - const std::string timingName = std::string(testName) + " asset " + std::to_string(assetNum) + " family " + std::to_string(familyNum) + " damage " + std::to_string(damageNum) + " accel " + std::to_string(accelType); - BlastBasePerfTestStrict::reportData(timingName + " material", timers.material); - history.push_back((uint32_t)actors.size()); - results.totalTime += timers.material; - history.push_back(0); // separator - } - - // Release remaining actors - std::for_each(actors.begin(), actors.end(), [](NvBlastActor* a) { NvBlastActorDeactivate(a, messageLog); }); - actors.clear(); - - alignedFree(family); - } - - if (accelerator) - accelerator->release(); - - // Release asset data - alignedFree(asset); - } - } - - return results; - } -}; - - -// Tests -TEST_F(PerfTest, DISABLED_DamageRadialSimple) -{ - const int trialCount = 10; - std::cout << "Trial (of " << trialCount << "): "; - for (int trial = 1; trial <= trialCount; ++trial) - { - if (trial % 100 == 0) - { - std::cout << trial << ".. "; - std::cout.flush(); - } - std::vector history1, history2; - - uint32_t assetCount = 4; - uint32_t familyCount = 4; - uint32_t damageCount = 4; - - PerfResults results0 = damageLeafSupportActors(test_info_->name(), assetCount, familyCount, damageCount, 0, history1); - BlastBasePerfTestStrict::reportData("DamageRadialSimple total0 " , results0.totalTime); - BlastBasePerfTestStrict::reportData("DamageRadialSimple create0 ", results0.createTime); - PerfResults results1 = damageLeafSupportActors(test_info_->name(), assetCount, familyCount, damageCount, 1, history2); - BlastBasePerfTestStrict::reportData("DamageRadialSimple total1 ", results1.totalTime); - BlastBasePerfTestStrict::reportData("DamageRadialSimple create1 ", results1.createTime); - - EXPECT_TRUE(history1 == history2); - } - std::cout << "done." << std::endl; -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "BlastBasePerfTest.h" +#include "NvBlastExtDamageShaders.h" +#include "NvBlastExtSerialization.h" +#include "NvBlastTime.h" +#include "PxVec3.h" +#include "PxBounds3.h" +#include +#include +#include + +using namespace Nv::Blast; +using namespace physx; + +static void blast +( + std::set& actorsToDamage, + GeneratorAsset* testAsset, + NvBlastExtDamageAccelerator* accelerator, + GeneratorAsset::Vec3 localPos, + float minRadius, float maxRadius, + float compressiveDamage, + std::vector& history, + NvBlastTimers& timers) +{ + std::vector chunkEvents; /* num lower-support chunks + bonds */ + std::vector bondEvents; /* num lower-support chunks + bonds */ + chunkEvents.resize(testAsset->solverChunks.size()); + bondEvents.resize(testAsset->solverBonds.size()); + + NvBlastExtRadialDamageDesc damage = { + compressiveDamage, + { localPos.x, localPos.y, localPos.z }, + minRadius, + maxRadius + }; + + NvBlastExtMaterial material; + + NvBlastExtProgramParams programParams = + { + &damage, + &material, + accelerator + }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + std::vector splitScratch; + std::vector newActors(testAsset->solverChunks.size()); + + size_t totalNewActorsCount = 0; + for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) + { + NvBlastActor* actor = *k; + + NvBlastFractureBuffers events = { (uint32_t)bondEvents.size(), (uint32_t)chunkEvents.size(), bondEvents.data(), chunkEvents.data() }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, &timers); + NvBlastActorApplyFracture(nullptr, actor, &events, nullptr, &timers); + + bool removeActor = false; + + if (events.bondFractureCount + events.chunkFractureCount > 0) + { + history.push_back(events.bondFractureCount + events.chunkFractureCount); + + splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = &newActors[totalNewActorsCount]; + const size_t bufferSize = newActors.size() - totalNewActorsCount; + const size_t newActorsCount = NvBlastActorSplit(&result, actor, (uint32_t)bufferSize, splitScratch.data(), nullptr, &timers); + totalNewActorsCount += newActorsCount; + removeActor = newActorsCount > 0; + } + + if (removeActor) + { + k = actorsToDamage.erase(k); + } + else + { + ++k; + } + } + + for (size_t i = 0; i < totalNewActorsCount; ++i) + { + actorsToDamage.insert(newActors[i]); + } +} + +typedef BlastBasePerfTest BlastBasePerfTestStrict; + + +struct PerfResults +{ + int64_t totalTime; + int64_t createTime; +}; + +class PerfTest : public BlastBasePerfTestStrict +{ +public: + + NvBlastAsset* loadAsset(const char* path, ExtSerialization* ser) + { + std::ifstream infileStream(path, std::ios::binary); + if (!infileStream.is_open()) + { + return nullptr; + } + const std::vector inBuffer((std::istreambuf_iterator(infileStream)), std::istreambuf_iterator()); + infileStream.close(); + + NvBlastAsset* asset = static_cast(ser->deserializeFromBuffer(inBuffer.data(), inBuffer.size())); + + return asset; + } + + PerfResults damageLeafSupportActors(const char* testName, uint32_t assetCount, uint32_t familyCount, uint32_t damageCount, int accelType, std::vector& history) + { + PerfResults results; + results.totalTime = 0; + results.createTime = 0; + + const float compressiveDamage = 1.0f; + const uint32_t minChunkCount = 1000; + const uint32_t maxChunkCount = 100000; + + srand(0); + + for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) + { + GeneratorAsset cube; + NvBlastAssetDesc desc; + generateRandomCube(cube, desc, minChunkCount, maxChunkCount); + + { + std::vector scratch; + physx::PxBounds3 bounds = physx::PxBounds3::empty(); +#if 1 + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); + void* mem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + bounds = physx::PxBounds3::centerExtents(physx::PxVec3(0, 0, 0), physx::PxVec3(cube.extents.x, cube.extents.y, cube.extents.z)); +#else + // load asset + NvBlastAsset* asset = nullptr; + ExtSerialization* ser = NvBlastExtSerializationCreate(); + for (int s = 0; s < 5 && !asset; s++) + { + asset = loadAsset(&"../../../../../test/assets/table.blast"[s * 3], ser); + } + EXPECT_TRUE(asset != nullptr); + ser->release(); + uint32_t bc = NvBlastAssetGetBondCount(asset, messageLog); + const NvBlastBond* bonds = NvBlastAssetGetBonds(asset, messageLog); + for (uint32_t i = 0; i < bc; i++) + { + bounds.include(reinterpret_cast(bonds[i].centroid)); + } +#endif + + Nv::Blast::Time t; + NvBlastExtDamageAccelerator* accelerator = NvBlastExtDamageAcceleratorCreate(asset, accelType); + results.createTime += t.getElapsedTicks(); + + // Generate familes + for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = nullptr; + actorDesc.uniformInitialBondHealth = 1.0f; + actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* mem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(mem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + EXPECT_TRUE(family != nullptr); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // Generate damage + std::set actors; + actors.insert(actor); + for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) + { + GeneratorAsset::Vec3 localPos = GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f) * 2; + localPos.x *= bounds.getExtents().x; + localPos.y *= bounds.getExtents().y; + localPos.z *= bounds.getExtents().z; + const float relativeDamageRadius = (float)rand() / RAND_MAX * bounds.getExtents().maxElement(); + + NvBlastTimers timers; + NvBlastTimersReset(&timers); + blast(actors, &cube, accelerator, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage, history, timers); + const std::string timingName = std::string(testName) + " asset " + std::to_string(assetNum) + " family " + std::to_string(familyNum) + " damage " + std::to_string(damageNum) + " accel " + std::to_string(accelType); + BlastBasePerfTestStrict::reportData(timingName + " material", timers.material); + history.push_back((uint32_t)actors.size()); + results.totalTime += timers.material; + history.push_back(0); // separator + } + + // Release remaining actors + std::for_each(actors.begin(), actors.end(), [](NvBlastActor* a) { NvBlastActorDeactivate(a, messageLog); }); + actors.clear(); + + alignedFree(family); + } + + if (accelerator) + accelerator->release(); + + // Release asset data + alignedFree(asset); + } + } + + return results; + } +}; + + +// Tests +TEST_F(PerfTest, DISABLED_DamageRadialSimple) +{ + const int trialCount = 10; + std::cout << "Trial (of " << trialCount << "): "; + for (int trial = 1; trial <= trialCount; ++trial) + { + if (trial % 100 == 0) + { + std::cout << trial << ".. "; + std::cout.flush(); + } + std::vector history1, history2; + + uint32_t assetCount = 4; + uint32_t familyCount = 4; + uint32_t damageCount = 4; + + PerfResults results0 = damageLeafSupportActors(test_info_->name(), assetCount, familyCount, damageCount, 0, history1); + BlastBasePerfTestStrict::reportData("DamageRadialSimple total0 " , results0.totalTime); + BlastBasePerfTestStrict::reportData("DamageRadialSimple create0 ", results0.createTime); + PerfResults results1 = damageLeafSupportActors(test_info_->name(), assetCount, familyCount, damageCount, 1, history2); + BlastBasePerfTestStrict::reportData("DamageRadialSimple total1 ", results1.totalTime); + BlastBasePerfTestStrict::reportData("DamageRadialSimple create1 ", results1.createTime); + + EXPECT_TRUE(history1 == history2); + } + std::cout << "done." << std::endl; +} diff --git a/test/src/perf/SolverPerfTests.cpp b/test/src/perf/SolverPerfTests.cpp old mode 100644 new mode 100755 index cad7928..ac04432 --- a/test/src/perf/SolverPerfTests.cpp +++ b/test/src/perf/SolverPerfTests.cpp @@ -1,203 +1,203 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBasePerfTest.h" -#include "TestAssets.h" -#include "NvBlastExtDamageShaders.h" -#include - - -static void blast -( - std::set& actorsToDamage, - GeneratorAsset* testAsset, - GeneratorAsset::Vec3 localPos, - float minRadius, float maxRadius, - float compressiveDamage, - NvBlastTimers& timers -) -{ - std::vector chunkEvents; /* num lower-support chunks + bonds */ - std::vector bondEvents; /* num lower-support chunks + bonds */ - chunkEvents.resize(testAsset->solverChunks.size()); - bondEvents.resize(testAsset->solverBonds.size()); - - NvBlastExtRadialDamageDesc damage[] = { - compressiveDamage, - { localPos.x, localPos.y, localPos.z }, - minRadius, - maxRadius - }; - - NvBlastExtProgramParams programParams = - { - damage, - nullptr - }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - nullptr - }; - - std::vector splitScratch; - std::vector newActors(testAsset->solverChunks.size()); - - size_t totalNewActorsCount = 0; - for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) - { - NvBlastActor* actor = *k; - - NvBlastFractureBuffers events = { (uint32_t)bondEvents.size(), (uint32_t)chunkEvents.size(), bondEvents.data(), chunkEvents.data() }; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, &timers); - NvBlastActorApplyFracture(&events, actor, &events, nullptr, &timers); - - bool removeActor = false; - - if (events.bondFractureCount + events.chunkFractureCount > 0) - { - splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = &newActors[totalNewActorsCount]; - const size_t bufferSize = newActors.size() - totalNewActorsCount; - const size_t newActorsCount = NvBlastActorSplit(&result, actor, (uint32_t)bufferSize, splitScratch.data(), nullptr, &timers); - totalNewActorsCount += newActorsCount; - removeActor = newActorsCount > 0; - } - - if (removeActor) - { - k = actorsToDamage.erase(k); - } - else - { - ++k; - } - } - - for (size_t i = 0; i < totalNewActorsCount; ++i) - { - actorsToDamage.insert(newActors[i]); - } -} - -typedef BlastBasePerfTest BlastBasePerfTestStrict; - -class PerfTest : public BlastBasePerfTestStrict -{ -public: - void damageLeafSupportActors(const char* testName, uint32_t assetCount, uint32_t familyCount, uint32_t damageCount) - { - const float relativeDamageRadius = 0.2f; - const float compressiveDamage = 1.0f; - const uint32_t minChunkCount = 100; - const uint32_t maxChunkCount = 10000; - - srand(0); - - for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) - { - GeneratorAsset cube; - NvBlastAssetDesc desc; - generateRandomCube(cube, desc, minChunkCount, maxChunkCount); - - { - std::vector scratch; - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); - void* mem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - - // Generate familes - for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) - { - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = nullptr; - actorDesc.uniformInitialBondHealth = 1.0f; - actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* mem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(mem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - EXPECT_TRUE(family != nullptr); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // Generate damage - std::set actors; - actors.insert(actor); - for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) - { - GeneratorAsset::Vec3 localPos = cube.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); - - NvBlastTimers timers; - NvBlastTimersReset(&timers); - blast(actors, &cube, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage, timers); - const std::string timingName = std::string(testName) + " asset " + std::to_string(assetNum) + " family " + std::to_string(familyNum) + " damage " + std::to_string(damageNum); - BlastBasePerfTestStrict::reportData(timingName + " material", timers.material); - BlastBasePerfTestStrict::reportData(timingName + " fracture", timers.fracture); - BlastBasePerfTestStrict::reportData(timingName + " island", timers.island); - BlastBasePerfTestStrict::reportData(timingName + " partition", timers.partition); - BlastBasePerfTestStrict::reportData(timingName + " visibility", timers.visibility); - } - - // Release remaining actors - std::for_each(actors.begin(), actors.end(), [](NvBlastActor* a){ NvBlastActorDeactivate(a, messageLog); }); - actors.clear(); - - alignedFree(family); - } - - // Release asset data - alignedFree(asset); - } - } - } -}; - -#if 0 -// Tests -TEST_F(PerfTest, DamageLeafSupportActorsTestVisibility) -{ - const int trialCount = 1000; - std::cout << "Trial (of " << trialCount << "): "; - for (int trial = 1; trial <= trialCount; ++trial) - { - if (trial % 100 == 0) - { - std::cout << trial << ".. "; - std::cout.flush(); - } - damageLeafSupportActors(test_info_->name(), 4, 4, 5); - } - std::cout << "done." << std::endl; -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "BlastBasePerfTest.h" +#include "TestAssets.h" +#include "NvBlastExtDamageShaders.h" +#include + + +static void blast +( + std::set& actorsToDamage, + GeneratorAsset* testAsset, + GeneratorAsset::Vec3 localPos, + float minRadius, float maxRadius, + float compressiveDamage, + NvBlastTimers& timers +) +{ + std::vector chunkEvents; /* num lower-support chunks + bonds */ + std::vector bondEvents; /* num lower-support chunks + bonds */ + chunkEvents.resize(testAsset->solverChunks.size()); + bondEvents.resize(testAsset->solverBonds.size()); + + NvBlastExtRadialDamageDesc damage[] = { + compressiveDamage, + { localPos.x, localPos.y, localPos.z }, + minRadius, + maxRadius + }; + + NvBlastExtProgramParams programParams = + { + damage, + nullptr + }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + std::vector splitScratch; + std::vector newActors(testAsset->solverChunks.size()); + + size_t totalNewActorsCount = 0; + for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) + { + NvBlastActor* actor = *k; + + NvBlastFractureBuffers events = { (uint32_t)bondEvents.size(), (uint32_t)chunkEvents.size(), bondEvents.data(), chunkEvents.data() }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, &timers); + NvBlastActorApplyFracture(&events, actor, &events, nullptr, &timers); + + bool removeActor = false; + + if (events.bondFractureCount + events.chunkFractureCount > 0) + { + splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = &newActors[totalNewActorsCount]; + const size_t bufferSize = newActors.size() - totalNewActorsCount; + const size_t newActorsCount = NvBlastActorSplit(&result, actor, (uint32_t)bufferSize, splitScratch.data(), nullptr, &timers); + totalNewActorsCount += newActorsCount; + removeActor = newActorsCount > 0; + } + + if (removeActor) + { + k = actorsToDamage.erase(k); + } + else + { + ++k; + } + } + + for (size_t i = 0; i < totalNewActorsCount; ++i) + { + actorsToDamage.insert(newActors[i]); + } +} + +typedef BlastBasePerfTest BlastBasePerfTestStrict; + +class PerfTest : public BlastBasePerfTestStrict +{ +public: + void damageLeafSupportActors(const char* testName, uint32_t assetCount, uint32_t familyCount, uint32_t damageCount) + { + const float relativeDamageRadius = 0.2f; + const float compressiveDamage = 1.0f; + const uint32_t minChunkCount = 100; + const uint32_t maxChunkCount = 10000; + + srand(0); + + for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) + { + GeneratorAsset cube; + NvBlastAssetDesc desc; + generateRandomCube(cube, desc, minChunkCount, maxChunkCount); + + { + std::vector scratch; + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); + void* mem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // Generate familes + for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = nullptr; + actorDesc.uniformInitialBondHealth = 1.0f; + actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* mem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(mem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + EXPECT_TRUE(family != nullptr); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // Generate damage + std::set actors; + actors.insert(actor); + for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) + { + GeneratorAsset::Vec3 localPos = cube.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); + + NvBlastTimers timers; + NvBlastTimersReset(&timers); + blast(actors, &cube, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage, timers); + const std::string timingName = std::string(testName) + " asset " + std::to_string(assetNum) + " family " + std::to_string(familyNum) + " damage " + std::to_string(damageNum); + BlastBasePerfTestStrict::reportData(timingName + " material", timers.material); + BlastBasePerfTestStrict::reportData(timingName + " fracture", timers.fracture); + BlastBasePerfTestStrict::reportData(timingName + " island", timers.island); + BlastBasePerfTestStrict::reportData(timingName + " partition", timers.partition); + BlastBasePerfTestStrict::reportData(timingName + " visibility", timers.visibility); + } + + // Release remaining actors + std::for_each(actors.begin(), actors.end(), [](NvBlastActor* a){ NvBlastActorDeactivate(a, messageLog); }); + actors.clear(); + + alignedFree(family); + } + + // Release asset data + alignedFree(asset); + } + } + } +}; + +#if 0 +// Tests +TEST_F(PerfTest, DamageLeafSupportActorsTestVisibility) +{ + const int trialCount = 1000; + std::cout << "Trial (of " << trialCount << "): "; + for (int trial = 1; trial <= trialCount; ++trial) + { + if (trial % 100 == 0) + { + std::cout << trial << ".. "; + std::cout.flush(); + } + damageLeafSupportActors(test_info_->name(), 4, 4, 5); + } + std::cout << "done." << std::endl; +} #endif \ No newline at end of file diff --git a/test/src/unit/APITests.cpp b/test/src/unit/APITests.cpp old mode 100644 new mode 100755 index 655a65c..d1ff4cd --- a/test/src/unit/APITests.cpp +++ b/test/src/unit/APITests.cpp @@ -1,1723 +1,1723 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBaseTest.h" -#include "NvBlastIndexFns.h" -#include "NvBlastExtDamageShaders.h" - -#include - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Utils / Tests Common -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -using namespace Nv::Blast; - -class APITest : public BlastBaseTest < NvBlastMessage::Error, 1 > -{ -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Tests -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -TEST_F(APITest, Basic) -{ - // create asset - const NvBlastAssetDesc& assetDesc = g_assetDescs[0]; - - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - NvBlastExtRadialDamageDesc damage = { - 10.0f, // compressive - { 0.0f, 0.0f, 0.0f }, // position - 4.0f, // min radius - maximum damage - 6.0f // max radius - zero damage - }; - - NvBlastBondFractureData outFracture[12]; /*num lower-support chunks + bonds?*/ - - NvBlastFractureBuffers events; - events.bondFractureCount = 12; - events.bondFractures = outFracture; - events.chunkFractureCount = 0; - events.chunkFractures = nullptr; - - NvBlastExtProgramParams programParams = { &damage, nullptr }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - nullptr - }; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, messageLog, nullptr); - NvBlastActorApplyFracture(&events, actor, &events, messageLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - EXPECT_EQ(12, events.bondFractureCount); - - NvBlastActor* newActors[8]; /* num lower-support chunks? plus space for deletedActor */ - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors; - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); - size_t newActorsCount = NvBlastActorSplit(&result, actor, 8, scratch.data(), messageLog, nullptr); - EXPECT_EQ(8, newActorsCount); - EXPECT_EQ(true, result.deletedActor == actor); - - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); - EXPECT_TRUE(actorReleaseResult); - } - alignedFree(family); - alignedFree(asset); -} - -TEST_F(APITest, DamageBondsCompressive) -{ - const size_t bondsCount = 6; - - const NvBlastChunkDesc c_chunks[8] = - { - // centroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 7 } - }; - - const NvBlastBondDesc c_bonds[bondsCount] = - { - { { {-1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 2.0f, 0.0f }, 0 }, { 1, 2 } }, - { { {-1.0f, 0.0f, 0.0f }, 1.0f, {-1.0f, 2.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-2.0f, 1.0f, 0.0f }, 0 }, { 3, 4 } }, - { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-2.0f,-1.0f, 0.0f }, 0 }, { 4, 5 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, {-1.0f,-2.0f, 0.0f }, 0 }, { 5, 6 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f,-2.0f, 0.0f }, 0 }, { 6, 7 } } - }; - - // create asset - const NvBlastAssetDesc assetDesc = { 8, c_chunks, bondsCount, c_bonds }; - - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // get graph nodes check - std::vector graphNodeIndices; - graphNodeIndices.resize(NvBlastActorGetGraphNodeCount(actor, nullptr)); - uint32_t graphNodesCount = NvBlastActorGetGraphNodeIndices(graphNodeIndices.data(), (uint32_t)graphNodeIndices.size(), actor, nullptr); - EXPECT_EQ(graphNodesCount, 7); - - NvBlastExtRadialDamageDesc damage = { - 1.0f, // compressive - { 4.0f, 2.0f, 0.0f }, // position - 4.0f, // min radius - maximum damage - 6.0f // max radius - zero damage - }; // linear falloff - - NvBlastBondFractureData outCommands[bondsCount] = { - { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, - { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, - { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, - { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, - { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, - { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, - }; - - NvBlastFractureBuffers commands = { - 6, 0, outCommands, nullptr - }; - - NvBlastExtProgramParams programParams = { &damage, nullptr }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - nullptr - }; - - NvBlastActorGenerateFracture(&commands, actor, program, &programParams, messageLog, nullptr); - - ASSERT_EQ(3, commands.bondFractureCount); - ASSERT_EQ(0, commands.chunkFractureCount); - - // node indices in _graph_ chunks - NvBlastBondFractureData expectedCommand[] = { - { 0, 0, 1, 1.0f }, - { 0, 1, 2, 0.5f }, - { 0, 5, 6, 0.5f } - }; - - for (int i = 0; i < 3; i++) - { - EXPECT_EQ(expectedCommand[i].nodeIndex0, outCommands[i].nodeIndex0); - EXPECT_EQ(expectedCommand[i].nodeIndex1, outCommands[i].nodeIndex1); - EXPECT_EQ(expectedCommand[i].health, outCommands[i].health); - } - - const bool actorReleaseResult = NvBlastActorDeactivate(actor, messageLog); - EXPECT_TRUE(actorReleaseResult); - alignedFree(family); - alignedFree(asset); -} - -TEST_F(APITest, DirectFractureKillsChunk) -{ - // 1--2 - // | | - // 3--4 <-- kill 4 - - const NvBlastChunkDesc c_chunks[9] = - { - // centroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 5 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 6 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 7 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 8 }, - }; - - const NvBlastBondDesc c_bonds[4] = - { - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 1.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-1.0f, 0.0f }, 0 }, { 3, 4 } }, - { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } }, - { { { 0.0f,-1.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 2, 4 } }, - }; - - NvBlastAssetDesc assetDesc; - assetDesc.chunkCount = 9; - assetDesc.chunkDescs = c_chunks; - assetDesc.bondCount = 4; - assetDesc.bondDescs = c_bonds; - - // create asset - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - NvBlastChunkFractureData fractureCmd; - fractureCmd.chunkIndex = 4; - fractureCmd.health = 1.0f; - - NvBlastFractureBuffers commands = { 0, 1, nullptr, &fractureCmd }; - - NvBlastChunkFractureData fractureEvt; - NvBlastFractureBuffers events = { 0, 1, nullptr, &fractureEvt }; - - NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - EXPECT_EQ(1, events.chunkFractureCount); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), messageLog, nullptr); - newActors.resize(newActorsCount); - - EXPECT_EQ(5, newActorsCount); - EXPECT_EQ(actor, result.deletedActor); - // check newActors contain original actor - EXPECT_TRUE(std::any_of(newActors.begin(), newActors.end(), [&](const NvBlastActor* a) { return actor == a; })); - - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); - EXPECT_TRUE(actorReleaseResult); - } - alignedFree(family); - alignedFree(asset); -} - -TEST_F(APITest, DirectFractureKillsIslandRootChunk) -{ - // 1--2 <-- kill 1 - // | | - // 3--4 - - const NvBlastChunkDesc c_chunks[9] = - { - // centroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 7 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 8 }, - }; - - const NvBlastBondDesc c_bonds[4] = - { - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 1.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-1.0f, 0.0f }, 0 }, { 3, 4 } }, - { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } }, - { { { 0.0f,-1.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 2, 4 } }, - }; - - NvBlastAssetDesc assetDesc; - assetDesc.chunkCount = 9; - assetDesc.chunkDescs = c_chunks; - assetDesc.bondCount = 4; - assetDesc.bondDescs = c_bonds; - - // create asset - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - NvBlastChunkFractureData fractureCmd; - fractureCmd.chunkIndex = 1; - fractureCmd.health = 1.0f; - - NvBlastFractureBuffers commands = { 0, 1, nullptr, &fractureCmd }; - - NvBlastChunkFractureData fractureEvt; - NvBlastFractureBuffers events = { 0, 1, nullptr, &fractureEvt }; - - NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - EXPECT_EQ(1, events.chunkFractureCount); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), messageLog, nullptr); - newActors.resize(newActorsCount); - - EXPECT_EQ(5, newActorsCount); - EXPECT_EQ(actor, result.deletedActor); - // check if newActors don't contain original actor - EXPECT_TRUE(!std::any_of(newActors.begin(), newActors.end(), [&](const NvBlastActor* a) { return actor == a; })); - - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); - EXPECT_TRUE(actorReleaseResult); - } - alignedFree(family); - alignedFree(asset); -} - -TEST_F(APITest, SubsupportFracture) -{ - const NvBlastAssetDesc& assetDesc = g_assetDescs[1]; // cube with subsupport - - // create asset with chunk map - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // first set of fracture commands - NvBlastChunkFractureData f1 = { 0, 1, 2.0f }; - NvBlastChunkFractureData f3 = { 0, 3, 0.5f }; - NvBlastChunkFractureData f5 = { 0, 5, 1.0f }; - NvBlastChunkFractureData f7 = { 0, 7, 1.0f }; - - std::vector chunkFractureData; - chunkFractureData.reserve(assetDesc.chunkCount); - chunkFractureData.push_back(f1); - chunkFractureData.push_back(f3); - chunkFractureData.push_back(f5); - chunkFractureData.push_back(f7); - ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); - ASSERT_EQ(4, chunkFractureData.size()); - - NvBlastFractureBuffers target = { 0, static_cast(chunkFractureData.capacity()), nullptr, chunkFractureData.data() }; - { - NvBlastFractureBuffers events = target; - NvBlastFractureBuffers commands = { 0, static_cast(chunkFractureData.size()), nullptr, chunkFractureData.data() }; - NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - ASSERT_EQ(4 + 8, events.chunkFractureCount); // all requested chunks take damage, and the children of one of them - } - - // re-apply same set of commands - chunkFractureData.clear(); - chunkFractureData.reserve(assetDesc.chunkCount); - chunkFractureData.push_back(f1); - chunkFractureData.push_back(f3); - chunkFractureData.push_back(f5); - chunkFractureData.push_back(f7); - ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); - ASSERT_EQ(4, chunkFractureData.size()); - - { - NvBlastFractureBuffers events = target; - NvBlastFractureBuffers commands = { 0, static_cast(chunkFractureData.size()), nullptr, chunkFractureData.data() }; - NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - ASSERT_EQ(1, events.chunkFractureCount); // f3 has broken the chunk - } - - // fracture all support chunks - // the chunks from the previous fractures must not be reported again (since they are all broken already) - NvBlastChunkFractureData f2 = { 0, 2, 2.0f }; // will damage chunk and children - NvBlastChunkFractureData f4 = { 0, 4, 0.5f }; // will damage chunk without creating children on split - NvBlastChunkFractureData f6 = { 0, 6, 2.0f }; // will damage chunk and children - NvBlastChunkFractureData f8 = { 0, 8, 1.0f }; // will damage chunk - - chunkFractureData.clear(); - chunkFractureData.reserve(assetDesc.chunkCount); - chunkFractureData.push_back(f1); - chunkFractureData.push_back(f2); - chunkFractureData.push_back(f3); - chunkFractureData.push_back(f4); - chunkFractureData.push_back(f5); - chunkFractureData.push_back(f6); - chunkFractureData.push_back(f7); - chunkFractureData.push_back(f8); - ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); - ASSERT_EQ(8, chunkFractureData.size()); - - NvBlastFractureBuffers events = target; - { - NvBlastFractureBuffers commands = { 0, static_cast(chunkFractureData.size()), nullptr, chunkFractureData.data() }; - NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - ASSERT_EQ(4 + 8 + 8, events.chunkFractureCount); // the new fracture commands all apply, plus two of them damage their children too - } - - for (size_t i = 0; i < events.chunkFractureCount; i++) - { - const uint32_t chunkIndex = events.chunkFractures[i].chunkIndex; - - ASSERT_TRUE(chunkIndex != 1); - ASSERT_TRUE(chunkIndex != 3); - ASSERT_TRUE(chunkIndex != 5); - ASSERT_TRUE(chunkIndex != 7); - - // literal values come from g_cube2ChunkDescs - bool isInSupportRange = chunkIndex <= 8 && chunkIndex >= 1; - bool isChildOfTwo = chunkIndex <= 24 && chunkIndex >= 17; - bool isChildOfSix = chunkIndex <= 56 && chunkIndex >= 49; - - ASSERT_TRUE(isInSupportRange || isChildOfTwo || isChildOfSix); - } - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), messageLog, nullptr); - newActors.resize(newActorsCount); - - EXPECT_EQ(64 - 8 + 1, newActorsCount); - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); - EXPECT_TRUE(actorReleaseResult); - } - alignedFree(family); - alignedFree(asset); -} - -static bool hasWarned = false; -static void myLog(int type, const char* msg, const char* file, int line) -{ - BlastBaseTest<-1, 0>::messageLog(type, msg, file, line); - hasWarned = true; -} -#define EXPECT_WARNING EXPECT_TRUE(hasWarned); hasWarned=false; -#define EXPECT_NO_WARNING EXPECT_FALSE(hasWarned); hasWarned=false; - -TEST_F(APITest, FractureNoEvents) -{ - static const uint32_t GUARD = 0xb1a57; - - const uint32_t chunksCount = 17; - const NvBlastChunkDesc c_chunks[chunksCount] = - { - // centroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, - }; - - const NvBlastBondDesc c_bonds[3] = - { - // normal, area, centroid, userdata, chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, - }; - - NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; - - // create asset with chunk map - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - std::vector cfData; - cfData.resize(0 + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector bfData; - - NvBlastChunkFractureData command[] = - { - { 0, 1, 10.0f }, - { 0, 2, 10.0f }, - }; - - NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - EXPECT_NO_WARNING; // events can be null - EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - newActors.resize(newActorsCount); - - EXPECT_EQ(9, newActorsCount); - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); - EXPECT_TRUE(actorReleaseResult); - } - - alignedFree(family); - alignedFree(asset); - - EXPECT_NO_WARNING; -} - -TEST_F(APITest, FractureBufferLimits) -{ - static const uint32_t GUARD = 0xb1a57; - - const uint32_t chunksCount = 17; - const NvBlastChunkDesc c_chunks[chunksCount] = - { - // centroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, - }; - - const NvBlastBondDesc c_bonds[3] = - { - // normal, area, centroid, userdata, chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, - }; - - NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; - { - // create asset with chunk map - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); - EXPECT_TRUE(asset != nullptr); - - for (uint32_t i = 0; i < 14; i++) - { - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - std::vector cfData; - cfData.resize(i + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector bfData; - - NvBlastChunkFractureData command[] = - { - { 0, 1, 10.0f }, - { 0, 2, 10.0f }, - }; - - NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; - NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; - - NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); - - EXPECT_WARNING; - EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); - EXPECT_EQ(i, events.chunkFractureCount); - for (uint32_t i = 0; i < events.chunkFractureCount; i++) - { - EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); - } - - EXPECT_TRUE(NvBlastActorDeactivate(actor, myLog)); - alignedFree(family); - } - - { - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - std::vector cfData; - cfData.resize(14 + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector bfData; - - NvBlastChunkFractureData command[] = - { - { 0, 1, 10.0f }, - { 0, 2, 10.0f }, - }; - - NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; - NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; - - NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - EXPECT_NO_WARNING; - EXPECT_EQ(14, events.chunkFractureCount); - for (uint32_t i = 0; i < events.chunkFractureCount; i++) - { - EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); - } - ASSERT_EQ(GUARD, cfData[cfData.size() - 1].userdata); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - newActors.resize(newActorsCount); - - EXPECT_EQ(9, newActorsCount); - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); - EXPECT_TRUE(actorReleaseResult); - } - - alignedFree(family); - } - - alignedFree(asset); - } - EXPECT_NO_WARNING; -} - -TEST_F(APITest, FractureBufferLimitsInSitu) -{ - static const uint32_t GUARD = 0xb1a57; - - const uint32_t chunksCount = 17; - const NvBlastChunkDesc c_chunks[chunksCount] = - { - // cenroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, - - { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, - }; - - const NvBlastBondDesc c_bonds[3] = - { - // normal, area, centroid, userdata, chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, - }; - - NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; - { - // create asset with chunk map - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); - EXPECT_TRUE(asset != nullptr); - - for (uint32_t i = 0; i < 14 - 2; i++) - { - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - std::vector cfData; - cfData.resize(2 + i + 1); - - std::vector bfData; - - cfData[0].userdata = 0; - cfData[0].chunkIndex = 1; - cfData[0].health = 10.0f; - - cfData[1].userdata = 0; - cfData[1].chunkIndex = 2; - cfData[1].health = 10.0f; - - cfData[2 + i].userdata = GUARD; - - NvBlastFractureBuffers commands = { 0, 2, nullptr, cfData.data() }; - NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; - - NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - EXPECT_WARNING; - EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); - EXPECT_EQ(2 + i, events.chunkFractureCount); - for (uint32_t i = 0; i < events.chunkFractureCount; i++) - { - EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); - } - - EXPECT_TRUE(NvBlastActorDeactivate(actor, myLog)); - - alignedFree(family); - } - - { - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - std::vector cfData; - cfData.resize(14 + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector bfData; - - cfData[0].userdata = 0; - cfData[0].chunkIndex = 1; - cfData[0].health = 10.0f; - - cfData[1].userdata = 0; - cfData[1].chunkIndex = 2; - cfData[1].health = 10.0f; - - cfData[14].userdata = GUARD; - - NvBlastFractureBuffers commands = { 0, 2, nullptr, cfData.data() }; - NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; - - NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - EXPECT_NO_WARNING; - EXPECT_EQ(14, events.chunkFractureCount); - for (uint32_t i = 0; i < events.chunkFractureCount; i++) - { - EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); - } - ASSERT_EQ(GUARD, cfData[cfData.size() - 1].userdata); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - newActors.resize(newActorsCount); - - EXPECT_EQ(9, newActorsCount); - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); - EXPECT_TRUE(actorReleaseResult); - } - alignedFree(family); - } - - alignedFree(asset); - } - EXPECT_NO_WARNING; -} - -/* -This test checks if bond or chunk fracture commands passed to NvBlastActorApplyFracture do not correspond to -the actor passed in they (commands) will be ignored and warning message will be fired. -*/ -TEST_F(APITest, FractureWarnAndFilterOtherActorCommands) -{ - const uint32_t chunksCount = 17; - const NvBlastChunkDesc c_chunks[chunksCount] = - { - // centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, - }; - - const NvBlastBondDesc c_bonds[4] = - { - // normal, area, centroid, userdata, chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } } - }; - - NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 4, c_bonds }; - - // create asset with chunk map - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - // split in 2 - std::vector actors; - { - NvBlastBondFractureData command[] = - { - { 0, 0, 2, 10.0f }, - { 0, 1, 2, 10.0f } - }; - - NvBlastFractureBuffers commands = { 2, 0, command, nullptr }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(2, newActorsCount); - EXPECT_EQ(actor, result.deletedActor); - - actors.insert(actors.begin(), result.newActors, result.newActors + newActorsCount); - } - - // damage bonds belonging to other actors, nothing expected to be broken - { - for (uint32_t i = 0; i < actors.size(); ++i) - { - NvBlastActor* actor = actors[i]; - NvBlastActor* otherActor = actors[(i + 1) % 2]; - - // get graph nodes check - std::vector graphNodeIndices; - graphNodeIndices.resize(NvBlastActorGetGraphNodeCount(otherActor, nullptr)); - uint32_t graphNodesCount = NvBlastActorGetGraphNodeIndices(graphNodeIndices.data(), (uint32_t)graphNodeIndices.size(), otherActor, nullptr); - EXPECT_EQ(graphNodesCount, 2); - - NvBlastBondFractureData command[] = - { - { 0, graphNodeIndices[0], graphNodeIndices[1], 10.0f } - }; - - NvBlastFractureBuffers commands = { 1, 0, command, nullptr }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_WARNING; - EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(0, newActorsCount); - EXPECT_EQ(nullptr, result.deletedActor); - } - } - - // damage bonds, split actors in 2 each - std::vector actors2; - { - for (uint32_t i = 0; i < 2; ++i) - { - NvBlastActor* actor = actors[i]; - - // get graph nodes check - std::vector graphNodeIndices; - graphNodeIndices.resize(NvBlastActorGetGraphNodeCount(actor, nullptr)); - uint32_t graphNodesCount = NvBlastActorGetGraphNodeIndices(graphNodeIndices.data(), (uint32_t)graphNodeIndices.size(), actor, nullptr); - EXPECT_EQ(graphNodesCount, 2); - - NvBlastBondFractureData command[] = - { - { 0, graphNodeIndices[0], graphNodeIndices[1], 10.0f } - }; - - NvBlastFractureBuffers commands = { 1, 0, command, nullptr }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(2, newActorsCount); - EXPECT_EQ(actor, result.deletedActor); - - actors2.insert(actors2.begin(), result.newActors, result.newActors + newActorsCount); - } - } - - // damage chunk belonging to other actor (expect no split or damage taken) - { - for (uint32_t i = 0; i < actors.size(); ++i) - { - NvBlastActor* actor = actors[i]; - NvBlastActor* otherActor = actors[(i + 1) % 2]; - - uint32_t chunkToDamage; - NvBlastActorGetVisibleChunkIndices(&chunkToDamage, 1, otherActor, myLog); - - NvBlastChunkFractureData command[] = - { - { 0, chunkToDamage, 0.9f }, - }; - - NvBlastFractureBuffers commands = { 0, 1, nullptr, command }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_WARNING; - EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(0, newActorsCount); - EXPECT_EQ(nullptr, result.deletedActor); - - EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); - uint32_t chunkIndex; - NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); - EXPECT_NE(chunkToDamage, chunkIndex); - } - } - - for (NvBlastActor* actor : actors2) - { - NvBlastActorDeactivate(actor, myLog); - } - - alignedFree(family); - alignedFree(asset); - - EXPECT_NO_WARNING; -} - -/** -If duplicate bonds are passed asset create routine will ignore them (but fire warning) -We pass duplicated bonds to world chunk and fully fracture actor once. -*/ -TEST_F(APITest, FractureWithBondDuplicates) -{ - const uint32_t chunksCount = 17; - const NvBlastChunkDesc c_chunks[chunksCount] = - { - // centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, - }; - - const uint32_t bondCount = 20; - const uint32_t world = ~(uint32_t)0; // world chunk => invalid index - const NvBlastBondDesc c_bonds[bondCount] = - { - // normal, area, centroid, userdata, chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 1, world } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 2, 1 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 2, world } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 2, world } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 3, 1 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 4, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 4, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 4, world } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 5, 1 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 5, world } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, 5 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, world } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, world } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 7, 5 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 7, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, 7 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, 6 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, world } } - }; - - NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, bondCount, c_bonds }; - - // create asset - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); - EXPECT_WARNING; - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - // split in 2 - std::vector actors; - { - NvBlastExtRadialDamageDesc damage = { - 10.0f, // compressive - { 0.0f, 0.0f, 0.0f }, // position - 100.0f, // min radius - maximum damage - 100.0f // max radius - zero damage - }; - - NvBlastBondFractureData outBondFracture[bondCount]; - NvBlastChunkFractureData outChunkFracture[chunksCount]; - - NvBlastFractureBuffers events; - events.bondFractureCount = 2; - events.bondFractures = outBondFracture; - events.chunkFractureCount = 2; - events.chunkFractures = outChunkFracture; - - NvBlastExtProgramParams programParams = { &damage, nullptr }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - NvBlastExtFalloffSubgraphShader - }; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, myLog, nullptr); - NvBlastActorApplyFracture(nullptr, actor, &events, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(8, newActorsCount); - EXPECT_EQ(actor, result.deletedActor); - - actors.insert(actors.begin(), result.newActors, result.newActors + newActorsCount); - } - - for (NvBlastActor* actor : actors) - { - NvBlastActorDeactivate(actor, myLog); - } - - alignedFree(family); - alignedFree(asset); - - EXPECT_NO_WARNING; -} - -#if 0 -TEST(APITest, UserChunkMap) -{ - for (int i = 0; i < 2; ++i) - { - // Choose descriptor list - const NvBlastAssetDesc* descs = nullptr; - size_t size = 0; - switch (i) - { - case 0: - descs = g_assetDescs; - size = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - break; - case 1: - descs = g_assetDescsMissingCoverage; - size = sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); - break; - default: - continue; - } - - // Iterate over list - for (size_t j = 0; j < size; ++j) - { - // Create asset - const NvBlastAssetDesc* desc = descs + j; - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(desc)); - std::vector chunkMap(desc->chunkCount); - NvBlastAsset* asset = NvBlastCreateAsset(&chunkMap[0], desc, alignedAlloc, scratch.data(), nullptr); - EXPECT_TRUE(asset); - - // Test map - Nv::Blast::Asset& a = static_cast(asset); - uint32_t supportChunkCount = 0; - uint32_t subsupportChunkCount = 0; - for (uint32_t i = 0; i < desc->chunkCount; ++i) - { - const uint32_t map = chunkMap[i]; - if (Nv::Blast::isInvalidIndex(map)) - { - continue; - } - else if (map < a.m_firstSubsupportChunkIndex) - { - EXPECT_LT(map, asset.m_graph.m_nodeCount); - ++supportChunkCount; - } - else - { - EXPECT_LT(map, asset.m_chunkCount); - EXPECT_GE(map, asset.m_graph.m_nodeCount); - ++subsupportChunkCount; - } - } - EXPECT_EQ(supportChunkCount, asset.m_graph.m_nodeCount); - EXPECT_EQ(subsupportChunkCount, a.getLowerSupportChunkCount() - asset.m_graph.m_nodeCount); - - // Release asset - NvBlastAssetRelease(asset, free, nullptr); - } - } -} -#endif - -TEST_F(APITest, NoBondsSausage) -{ - // create asset - const NvBlastChunkDesc c_chunks[4] = - { - // centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 2 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 3 } - }; - - NvBlastAssetDesc assetDesc; - assetDesc.chunkCount = 4; - assetDesc.chunkDescs = c_chunks; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); - const NvBlastChunk* chunks = NvBlastAssetGetChunks(asset, messageLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // check visible chunk - { - EXPECT_EQ(NvBlastActorGetVisibleChunkCount(actor, messageLog), 1); - uint32_t chunkIndex; - NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, messageLog); - EXPECT_EQ(chunks[chunkIndex].userData, 0); - } - - // damage - NvBlastExtRadialDamageDesc damage = { - 10.0f, // compressive - { 0.0f, 0.0f, 0.0f }, // position - 4.0f, // min radius - maximum damage - 6.0f // max radius - zero damage - }; - - NvBlastBondFractureData outBondFracture[2]; - NvBlastChunkFractureData outChunkFracture[2]; - - NvBlastFractureBuffers events; - events.bondFractureCount = 2; - events.bondFractures = outBondFracture; - events.chunkFractureCount = 2; - events.chunkFractures = outChunkFracture; - - NvBlastExtProgramParams programParams = { &damage, nullptr }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - NvBlastExtFalloffSubgraphShader - }; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, messageLog, nullptr); - NvBlastActorApplyFracture(&events, actor, &events, messageLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - EXPECT_EQ(0, events.bondFractureCount); - EXPECT_EQ(1, events.chunkFractureCount); - - // split - NvBlastActor* newActors[8]; /* num lower-support chunks? plus space for deletedActor */ - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors; - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); - size_t newActorsCount = NvBlastActorSplit(&result, actor, 8, scratch.data(), messageLog, nullptr); - EXPECT_EQ(1, newActorsCount); - EXPECT_EQ(true, result.deletedActor == actor); - - // check visible chunk - { - EXPECT_EQ(NvBlastActorGetVisibleChunkCount(result.newActors[0], messageLog), 1); - uint32_t chunkIndex; - NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, result.newActors[0], messageLog); - EXPECT_EQ(chunks[chunkIndex].userData, 3); - } - - // release all - for (uint32_t i = 0; i < newActorsCount; ++i) - { - const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); - EXPECT_TRUE(actorReleaseResult); - } - - alignedFree(family); - alignedFree(asset); -} - -TEST_F(APITest, SplitOnlyWhenNecessary) -{ - static const uint32_t GUARD = 0xb1a57; - - const uint32_t chunksCount = 17; - const NvBlastChunkDesc c_chunks[chunksCount] = - { - // centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, - - { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, - { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, - }; - - const NvBlastBondDesc c_bonds[4] = - { - // normal, area, centroid, userdata, chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } } - }; - - NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 4, c_bonds }; - - // create asset with chunk map - std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); - void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); - EXPECT_TRUE(asset != nullptr); - - // create actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); - EXPECT_TRUE(actor != nullptr); - - - // damage health only (expect no split) - { - NvBlastBondFractureData command[] = - { - { 0, 0, 1, 0.99f }, - { 0, 1, 2, 0.50f }, - { 0, 2, 3, 0.01f } - }; - - NvBlastFractureBuffers commands = { 3, 0, command, nullptr }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(0, newActorsCount); - EXPECT_EQ(nullptr, result.deletedActor); - - EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); - uint32_t chunkIndex; - NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); - EXPECT_EQ(0, chunkIndex); - } - - // break 1 bond (expect no split) - { - NvBlastBondFractureData command[] = - { - { 0, 0, 2, 10.0f }, - }; - - NvBlastFractureBuffers commands = { 1, 0, command, nullptr }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(0, newActorsCount); - EXPECT_EQ(nullptr, result.deletedActor); - - EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); - uint32_t chunkIndex; - NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); - EXPECT_EQ(0, chunkIndex); - } - - // split in 4 - std::vector actors; - { - NvBlastBondFractureData command[] = - { - { 0, 0, 1, 10.0f }, - { 0, 1, 2, 10.0f }, - { 0, 2, 3, 10.0f } - }; - - NvBlastFractureBuffers commands = { 3, 0, command, nullptr }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(4, newActorsCount); - EXPECT_EQ(actor, result.deletedActor); - - actors.insert(actors.begin(), result.newActors, result.newActors + newActorsCount); - } - - // damage chunk's health only (expect no split) - { - for (NvBlastActor* actor : actors) - { - uint32_t chunkToDamage; - NvBlastActorGetVisibleChunkIndices(&chunkToDamage, 1, actor, myLog); - - NvBlastChunkFractureData command[] = - { - { 0, chunkToDamage, 0.9f }, - }; - - NvBlastFractureBuffers commands = { 0, 1, nullptr, command }; - NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); - EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); - - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); - std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(0, newActorsCount); - EXPECT_EQ(nullptr, result.deletedActor); - - EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); - uint32_t chunkIndex; - NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); - EXPECT_EQ(chunkToDamage, chunkIndex); - } - } - - for (NvBlastActor* actor : actors) - { - NvBlastActorDeactivate(actor, myLog); - } - - alignedFree(family); - alignedFree(asset); - - EXPECT_NO_WARNING; -} - -#if NV_WINDOWS_FAMILY -#include -TEST_F(APITest,CExportsNoNameMangling) -{ - - // - // tests the lib-link-free approach using unmangled names (extern "C") - // - -#if NV_WIN32 -#if NV_DEBUG - const char* dllName = "NvBlastDebug_x86.dll"; -#elif NV_CHECKED - const char* dllName = "NvBlastChecked_x86.dll"; -#elif NV_PROFILE - const char* dllName = "NvBlastProfile_x86.dll"; -#else - const char* dllName = "NvBlast_x86.dll"; -#endif -#elif NV_WIN64 -#if NV_DEBUG - const char* dllName = "NvBlastDebug_x64.dll"; -#elif NV_CHECKED - const char* dllName = "NvBlastChecked_x64.dll"; -#elif NV_PROFILE - const char* dllName = "NvBlastProfile_x64.dll"; -#else - const char* dllName = "NvBlast_x64.dll"; -#endif -#endif - - HMODULE dllHandle = LoadLibrary(TEXT(dllName)); - DWORD error = GetLastError(); - ASSERT_TRUE(dllHandle != nullptr); - - - // Asset functions - typedef size_t(*NvBlastGetRequiredScratchForCreateAsset)(const NvBlastAssetDesc* desc); - typedef size_t(*NvBlastGetAssetMemorySize)(const NvBlastAssetDesc* desc); - typedef NvBlastAsset*(*NvBlastCreateAsset)(void* mem, const NvBlastAssetDesc* desc, void* scratch, NvBlastLog logFn); - - NvBlastGetRequiredScratchForCreateAsset assetCreateRequiredScratch = (NvBlastGetRequiredScratchForCreateAsset)GetProcAddress(dllHandle, TEXT("NvBlastGetRequiredScratchForCreateAsset")); - ASSERT_TRUE(assetCreateRequiredScratch != nullptr); - - NvBlastGetAssetMemorySize assetGetMemorySize = (NvBlastGetAssetMemorySize)GetProcAddress(dllHandle, TEXT("NvBlastGetAssetMemorySize")); - ASSERT_TRUE(assetGetMemorySize != nullptr); - - NvBlastCreateAsset assetCreate = (NvBlastCreateAsset)GetProcAddress(dllHandle, TEXT("NvBlastCreateAsset")); - ASSERT_TRUE(assetCreate != nullptr); - - // Family functions - typedef NvBlastFamily* (*NvBlastAssetCreateFamily)(void* mem, const NvBlastAsset* asset, NvBlastLog logFn); - typedef size_t(*NVBLASTASSETGETFAMILYMEMORYSIZE)(const NvBlastAsset* asset); - - NVBLASTASSETGETFAMILYMEMORYSIZE familyGetMemorySize = (NVBLASTASSETGETFAMILYMEMORYSIZE)GetProcAddress(dllHandle, TEXT("NvBlastAssetGetFamilyMemorySize")); - ASSERT_TRUE(familyGetMemorySize != nullptr); - - NvBlastAssetCreateFamily familyCreate = (NvBlastAssetCreateFamily)GetProcAddress(dllHandle, TEXT("NvBlastAssetCreateFamily")); - ASSERT_TRUE(familyCreate != nullptr); - - - // Actor functions - typedef size_t(*NvBlastFamilyGetRequiredScratchForCreateFirstActor)(const NvBlastFamily* family); - typedef NvBlastActor* (*NvBlastFamilyCreateFirstActor)(NvBlastFamily* family, const NvBlastActorDesc* desc, void* scratch, NvBlastLog logFn); - typedef bool(*NVBLASTACTORDEACTIVATE)(NvBlastActor* actor); - - NvBlastFamilyGetRequiredScratchForCreateFirstActor actorcreaterequiredscratch = (NvBlastFamilyGetRequiredScratchForCreateFirstActor)GetProcAddress(dllHandle, TEXT("NvBlastFamilyGetRequiredScratchForCreateFirstActor")); - ASSERT_TRUE(actorcreaterequiredscratch != nullptr); - - NvBlastFamilyCreateFirstActor actorCreate = (NvBlastFamilyCreateFirstActor)GetProcAddress(dllHandle, TEXT("NvBlastFamilyCreateFirstActor")); - ASSERT_TRUE(actorCreate != nullptr); - - NVBLASTACTORDEACTIVATE actorRelease = (NVBLASTACTORDEACTIVATE)GetProcAddress(dllHandle, TEXT("NvBlastActorDeactivate")); - ASSERT_TRUE(actorRelease != nullptr); - - - const NvBlastChunkDesc c_chunks[] = - { - // centroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, - { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, - }; - - NvBlastAssetDesc assetDesc; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.chunkCount = 4; - assetDesc.chunkDescs = c_chunks; - - NvBlastAsset* asset; - { - size_t requiredsize = assetCreateRequiredScratch(&assetDesc); - std::vectorscratch(requiredsize); - void* mem = alignedZeroedAlloc(assetGetMemorySize(&assetDesc)); - asset = assetCreate(mem, &assetDesc, scratch.data(), myLog); - ASSERT_TRUE(asset != nullptr); - } - - void* fmem = alignedZeroedAlloc(familyGetMemorySize(asset)); - NvBlastFamily* family = familyCreate(fmem, asset, myLog); - - { - NvBlastActorDesc actorD; - actorD.initialBondHealths = actorD.initialSupportChunkHealths = nullptr; - actorD.uniformInitialBondHealth = actorD.uniformInitialLowerSupportChunkHealth = 1.0f; - - size_t requiredsize = actorcreaterequiredscratch(family); - std::vectorscratch(requiredsize); - NvBlastActor* actor = actorCreate(family, &actorD, scratch.data(), myLog); - ASSERT_TRUE(actor != nullptr); - - ASSERT_TRUE(actorRelease(actor)); - } - - alignedFree(family); - alignedFree(asset); - - EXPECT_NO_WARNING; -} -#endif +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "BlastBaseTest.h" +#include "NvBlastIndexFns.h" +#include "NvBlastExtDamageShaders.h" + +#include + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Utils / Tests Common +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace Nv::Blast; + +class APITest : public BlastBaseTest < NvBlastMessage::Error, 1 > +{ +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tests +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST_F(APITest, Basic) +{ + // create asset + const NvBlastAssetDesc& assetDesc = g_assetDescs[0]; + + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + NvBlastExtRadialDamageDesc damage = { + 10.0f, // compressive + { 0.0f, 0.0f, 0.0f }, // position + 4.0f, // min radius - maximum damage + 6.0f // max radius - zero damage + }; + + NvBlastBondFractureData outFracture[12]; /*num lower-support chunks + bonds?*/ + + NvBlastFractureBuffers events; + events.bondFractureCount = 12; + events.bondFractures = outFracture; + events.chunkFractureCount = 0; + events.chunkFractures = nullptr; + + NvBlastExtProgramParams programParams = { &damage, nullptr }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, messageLog, nullptr); + NvBlastActorApplyFracture(&events, actor, &events, messageLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + EXPECT_EQ(12, events.bondFractureCount); + + NvBlastActor* newActors[8]; /* num lower-support chunks? plus space for deletedActor */ + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors; + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + size_t newActorsCount = NvBlastActorSplit(&result, actor, 8, scratch.data(), messageLog, nullptr); + EXPECT_EQ(8, newActorsCount); + EXPECT_EQ(true, result.deletedActor == actor); + + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + alignedFree(family); + alignedFree(asset); +} + +TEST_F(APITest, DamageBondsCompressive) +{ + const size_t bondsCount = 6; + + const NvBlastChunkDesc c_chunks[8] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 7 } + }; + + const NvBlastBondDesc c_bonds[bondsCount] = + { + { { {-1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 2.0f, 0.0f }, 0 }, { 1, 2 } }, + { { {-1.0f, 0.0f, 0.0f }, 1.0f, {-1.0f, 2.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-2.0f, 1.0f, 0.0f }, 0 }, { 3, 4 } }, + { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-2.0f,-1.0f, 0.0f }, 0 }, { 4, 5 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, {-1.0f,-2.0f, 0.0f }, 0 }, { 5, 6 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f,-2.0f, 0.0f }, 0 }, { 6, 7 } } + }; + + // create asset + const NvBlastAssetDesc assetDesc = { 8, c_chunks, bondsCount, c_bonds }; + + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // get graph nodes check + std::vector graphNodeIndices; + graphNodeIndices.resize(NvBlastActorGetGraphNodeCount(actor, nullptr)); + uint32_t graphNodesCount = NvBlastActorGetGraphNodeIndices(graphNodeIndices.data(), (uint32_t)graphNodeIndices.size(), actor, nullptr); + EXPECT_EQ(graphNodesCount, 7); + + NvBlastExtRadialDamageDesc damage = { + 1.0f, // compressive + { 4.0f, 2.0f, 0.0f }, // position + 4.0f, // min radius - maximum damage + 6.0f // max radius - zero damage + }; // linear falloff + + NvBlastBondFractureData outCommands[bondsCount] = { + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + }; + + NvBlastFractureBuffers commands = { + 6, 0, outCommands, nullptr + }; + + NvBlastExtProgramParams programParams = { &damage, nullptr }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + NvBlastActorGenerateFracture(&commands, actor, program, &programParams, messageLog, nullptr); + + ASSERT_EQ(3, commands.bondFractureCount); + ASSERT_EQ(0, commands.chunkFractureCount); + + // node indices in _graph_ chunks + NvBlastBondFractureData expectedCommand[] = { + { 0, 0, 1, 1.0f }, + { 0, 1, 2, 0.5f }, + { 0, 5, 6, 0.5f } + }; + + for (int i = 0; i < 3; i++) + { + EXPECT_EQ(expectedCommand[i].nodeIndex0, outCommands[i].nodeIndex0); + EXPECT_EQ(expectedCommand[i].nodeIndex1, outCommands[i].nodeIndex1); + EXPECT_EQ(expectedCommand[i].health, outCommands[i].health); + } + + const bool actorReleaseResult = NvBlastActorDeactivate(actor, messageLog); + EXPECT_TRUE(actorReleaseResult); + alignedFree(family); + alignedFree(asset); +} + +TEST_F(APITest, DirectFractureKillsChunk) +{ + // 1--2 + // | | + // 3--4 <-- kill 4 + + const NvBlastChunkDesc c_chunks[9] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 8 }, + }; + + const NvBlastBondDesc c_bonds[4] = + { + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 1.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-1.0f, 0.0f }, 0 }, { 3, 4 } }, + { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } }, + { { { 0.0f,-1.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 2, 4 } }, + }; + + NvBlastAssetDesc assetDesc; + assetDesc.chunkCount = 9; + assetDesc.chunkDescs = c_chunks; + assetDesc.bondCount = 4; + assetDesc.bondDescs = c_bonds; + + // create asset + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + NvBlastChunkFractureData fractureCmd; + fractureCmd.chunkIndex = 4; + fractureCmd.health = 1.0f; + + NvBlastFractureBuffers commands = { 0, 1, nullptr, &fractureCmd }; + + NvBlastChunkFractureData fractureEvt; + NvBlastFractureBuffers events = { 0, 1, nullptr, &fractureEvt }; + + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + EXPECT_EQ(1, events.chunkFractureCount); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), messageLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(5, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + // check newActors contain original actor + EXPECT_TRUE(std::any_of(newActors.begin(), newActors.end(), [&](const NvBlastActor* a) { return actor == a; })); + + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + alignedFree(family); + alignedFree(asset); +} + +TEST_F(APITest, DirectFractureKillsIslandRootChunk) +{ + // 1--2 <-- kill 1 + // | | + // 3--4 + + const NvBlastChunkDesc c_chunks[9] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 8 }, + }; + + const NvBlastBondDesc c_bonds[4] = + { + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 1.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-1.0f, 0.0f }, 0 }, { 3, 4 } }, + { { { 0.0f,-1.0f, 0.0f }, 1.0f, {-1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } }, + { { { 0.0f,-1.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 2, 4 } }, + }; + + NvBlastAssetDesc assetDesc; + assetDesc.chunkCount = 9; + assetDesc.chunkDescs = c_chunks; + assetDesc.bondCount = 4; + assetDesc.bondDescs = c_bonds; + + // create asset + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + NvBlastChunkFractureData fractureCmd; + fractureCmd.chunkIndex = 1; + fractureCmd.health = 1.0f; + + NvBlastFractureBuffers commands = { 0, 1, nullptr, &fractureCmd }; + + NvBlastChunkFractureData fractureEvt; + NvBlastFractureBuffers events = { 0, 1, nullptr, &fractureEvt }; + + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + EXPECT_EQ(1, events.chunkFractureCount); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), messageLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(5, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + // check if newActors don't contain original actor + EXPECT_TRUE(!std::any_of(newActors.begin(), newActors.end(), [&](const NvBlastActor* a) { return actor == a; })); + + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + alignedFree(family); + alignedFree(asset); +} + +TEST_F(APITest, SubsupportFracture) +{ + const NvBlastAssetDesc& assetDesc = g_assetDescs[1]; // cube with subsupport + + // create asset with chunk map + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // first set of fracture commands + NvBlastChunkFractureData f1 = { 0, 1, 2.0f }; + NvBlastChunkFractureData f3 = { 0, 3, 0.5f }; + NvBlastChunkFractureData f5 = { 0, 5, 1.0f }; + NvBlastChunkFractureData f7 = { 0, 7, 1.0f }; + + std::vector chunkFractureData; + chunkFractureData.reserve(assetDesc.chunkCount); + chunkFractureData.push_back(f1); + chunkFractureData.push_back(f3); + chunkFractureData.push_back(f5); + chunkFractureData.push_back(f7); + ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); + ASSERT_EQ(4, chunkFractureData.size()); + + NvBlastFractureBuffers target = { 0, static_cast(chunkFractureData.capacity()), nullptr, chunkFractureData.data() }; + { + NvBlastFractureBuffers events = target; + NvBlastFractureBuffers commands = { 0, static_cast(chunkFractureData.size()), nullptr, chunkFractureData.data() }; + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + ASSERT_EQ(4 + 8, events.chunkFractureCount); // all requested chunks take damage, and the children of one of them + } + + // re-apply same set of commands + chunkFractureData.clear(); + chunkFractureData.reserve(assetDesc.chunkCount); + chunkFractureData.push_back(f1); + chunkFractureData.push_back(f3); + chunkFractureData.push_back(f5); + chunkFractureData.push_back(f7); + ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); + ASSERT_EQ(4, chunkFractureData.size()); + + { + NvBlastFractureBuffers events = target; + NvBlastFractureBuffers commands = { 0, static_cast(chunkFractureData.size()), nullptr, chunkFractureData.data() }; + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + ASSERT_EQ(1, events.chunkFractureCount); // f3 has broken the chunk + } + + // fracture all support chunks + // the chunks from the previous fractures must not be reported again (since they are all broken already) + NvBlastChunkFractureData f2 = { 0, 2, 2.0f }; // will damage chunk and children + NvBlastChunkFractureData f4 = { 0, 4, 0.5f }; // will damage chunk without creating children on split + NvBlastChunkFractureData f6 = { 0, 6, 2.0f }; // will damage chunk and children + NvBlastChunkFractureData f8 = { 0, 8, 1.0f }; // will damage chunk + + chunkFractureData.clear(); + chunkFractureData.reserve(assetDesc.chunkCount); + chunkFractureData.push_back(f1); + chunkFractureData.push_back(f2); + chunkFractureData.push_back(f3); + chunkFractureData.push_back(f4); + chunkFractureData.push_back(f5); + chunkFractureData.push_back(f6); + chunkFractureData.push_back(f7); + chunkFractureData.push_back(f8); + ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); + ASSERT_EQ(8, chunkFractureData.size()); + + NvBlastFractureBuffers events = target; + { + NvBlastFractureBuffers commands = { 0, static_cast(chunkFractureData.size()), nullptr, chunkFractureData.data() }; + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + ASSERT_EQ(4 + 8 + 8, events.chunkFractureCount); // the new fracture commands all apply, plus two of them damage their children too + } + + for (size_t i = 0; i < events.chunkFractureCount; i++) + { + const uint32_t chunkIndex = events.chunkFractures[i].chunkIndex; + + ASSERT_TRUE(chunkIndex != 1); + ASSERT_TRUE(chunkIndex != 3); + ASSERT_TRUE(chunkIndex != 5); + ASSERT_TRUE(chunkIndex != 7); + + // literal values come from g_cube2ChunkDescs + bool isInSupportRange = chunkIndex <= 8 && chunkIndex >= 1; + bool isChildOfTwo = chunkIndex <= 24 && chunkIndex >= 17; + bool isChildOfSix = chunkIndex <= 56 && chunkIndex >= 49; + + ASSERT_TRUE(isInSupportRange || isChildOfTwo || isChildOfSix); + } + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), messageLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(64 - 8 + 1, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + alignedFree(family); + alignedFree(asset); +} + +static bool hasWarned = false; +static void myLog(int type, const char* msg, const char* file, int line) +{ + BlastBaseTest<-1, 0>::messageLog(type, msg, file, line); + hasWarned = true; +} +#define EXPECT_WARNING EXPECT_TRUE(hasWarned); hasWarned=false; +#define EXPECT_NO_WARNING EXPECT_FALSE(hasWarned); hasWarned=false; + +TEST_F(APITest, FractureNoEvents) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[3] = + { + // normal, area, centroid, userdata, chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; + + // create asset with chunk map + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector cfData; + cfData.resize(0 + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector bfData; + + NvBlastChunkFractureData command[] = + { + { 0, 1, 10.0f }, + { 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + EXPECT_NO_WARNING; // events can be null + EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(9, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); + EXPECT_TRUE(actorReleaseResult); + } + + alignedFree(family); + alignedFree(asset); + + EXPECT_NO_WARNING; +} + +TEST_F(APITest, FractureBufferLimits) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[3] = + { + // normal, area, centroid, userdata, chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; + { + // create asset with chunk map + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + for (uint32_t i = 0; i < 14; i++) + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector cfData; + cfData.resize(i + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector bfData; + + NvBlastChunkFractureData command[] = + { + { 0, 1, 10.0f }, + { 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; + NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + + EXPECT_WARNING; + EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + EXPECT_EQ(i, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + + EXPECT_TRUE(NvBlastActorDeactivate(actor, myLog)); + alignedFree(family); + } + + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector cfData; + cfData.resize(14 + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector bfData; + + NvBlastChunkFractureData command[] = + { + { 0, 1, 10.0f }, + { 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; + NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + EXPECT_NO_WARNING; + EXPECT_EQ(14, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + ASSERT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(9, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); + EXPECT_TRUE(actorReleaseResult); + } + + alignedFree(family); + } + + alignedFree(asset); + } + EXPECT_NO_WARNING; +} + +TEST_F(APITest, FractureBufferLimitsInSitu) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // cenroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[3] = + { + // normal, area, centroid, userdata, chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; + { + // create asset with chunk map + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + for (uint32_t i = 0; i < 14 - 2; i++) + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector cfData; + cfData.resize(2 + i + 1); + + std::vector bfData; + + cfData[0].userdata = 0; + cfData[0].chunkIndex = 1; + cfData[0].health = 10.0f; + + cfData[1].userdata = 0; + cfData[1].chunkIndex = 2; + cfData[1].health = 10.0f; + + cfData[2 + i].userdata = GUARD; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, cfData.data() }; + NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + EXPECT_WARNING; + EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + EXPECT_EQ(2 + i, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + + EXPECT_TRUE(NvBlastActorDeactivate(actor, myLog)); + + alignedFree(family); + } + + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector cfData; + cfData.resize(14 + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector bfData; + + cfData[0].userdata = 0; + cfData[0].chunkIndex = 1; + cfData[0].health = 10.0f; + + cfData[1].userdata = 0; + cfData[1].chunkIndex = 2; + cfData[1].health = 10.0f; + + cfData[14].userdata = GUARD; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, cfData.data() }; + NvBlastFractureBuffers events = { static_cast(bfData.size()), static_cast(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + EXPECT_NO_WARNING; + EXPECT_EQ(14, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + ASSERT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(9, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); + EXPECT_TRUE(actorReleaseResult); + } + alignedFree(family); + } + + alignedFree(asset); + } + EXPECT_NO_WARNING; +} + +/* +This test checks if bond or chunk fracture commands passed to NvBlastActorApplyFracture do not correspond to +the actor passed in they (commands) will be ignored and warning message will be fired. +*/ +TEST_F(APITest, FractureWarnAndFilterOtherActorCommands) +{ + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[4] = + { + // normal, area, centroid, userdata, chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } } + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 4, c_bonds }; + + // create asset with chunk map + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + // split in 2 + std::vector actors; + { + NvBlastBondFractureData command[] = + { + { 0, 0, 2, 10.0f }, + { 0, 1, 2, 10.0f } + }; + + NvBlastFractureBuffers commands = { 2, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(2, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + + actors.insert(actors.begin(), result.newActors, result.newActors + newActorsCount); + } + + // damage bonds belonging to other actors, nothing expected to be broken + { + for (uint32_t i = 0; i < actors.size(); ++i) + { + NvBlastActor* actor = actors[i]; + NvBlastActor* otherActor = actors[(i + 1) % 2]; + + // get graph nodes check + std::vector graphNodeIndices; + graphNodeIndices.resize(NvBlastActorGetGraphNodeCount(otherActor, nullptr)); + uint32_t graphNodesCount = NvBlastActorGetGraphNodeIndices(graphNodeIndices.data(), (uint32_t)graphNodeIndices.size(), otherActor, nullptr); + EXPECT_EQ(graphNodesCount, 2); + + NvBlastBondFractureData command[] = + { + { 0, graphNodeIndices[0], graphNodeIndices[1], 10.0f } + }; + + NvBlastFractureBuffers commands = { 1, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_WARNING; + EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + } + } + + // damage bonds, split actors in 2 each + std::vector actors2; + { + for (uint32_t i = 0; i < 2; ++i) + { + NvBlastActor* actor = actors[i]; + + // get graph nodes check + std::vector graphNodeIndices; + graphNodeIndices.resize(NvBlastActorGetGraphNodeCount(actor, nullptr)); + uint32_t graphNodesCount = NvBlastActorGetGraphNodeIndices(graphNodeIndices.data(), (uint32_t)graphNodeIndices.size(), actor, nullptr); + EXPECT_EQ(graphNodesCount, 2); + + NvBlastBondFractureData command[] = + { + { 0, graphNodeIndices[0], graphNodeIndices[1], 10.0f } + }; + + NvBlastFractureBuffers commands = { 1, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(2, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + + actors2.insert(actors2.begin(), result.newActors, result.newActors + newActorsCount); + } + } + + // damage chunk belonging to other actor (expect no split or damage taken) + { + for (uint32_t i = 0; i < actors.size(); ++i) + { + NvBlastActor* actor = actors[i]; + NvBlastActor* otherActor = actors[(i + 1) % 2]; + + uint32_t chunkToDamage; + NvBlastActorGetVisibleChunkIndices(&chunkToDamage, 1, otherActor, myLog); + + NvBlastChunkFractureData command[] = + { + { 0, chunkToDamage, 0.9f }, + }; + + NvBlastFractureBuffers commands = { 0, 1, nullptr, command }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_WARNING; + EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + + EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); + EXPECT_NE(chunkToDamage, chunkIndex); + } + } + + for (NvBlastActor* actor : actors2) + { + NvBlastActorDeactivate(actor, myLog); + } + + alignedFree(family); + alignedFree(asset); + + EXPECT_NO_WARNING; +} + +/** +If duplicate bonds are passed asset create routine will ignore them (but fire warning) +We pass duplicated bonds to world chunk and fully fracture actor once. +*/ +TEST_F(APITest, FractureWithBondDuplicates) +{ + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const uint32_t bondCount = 20; + const uint32_t world = ~(uint32_t)0; // world chunk => invalid index + const NvBlastBondDesc c_bonds[bondCount] = + { + // normal, area, centroid, userdata, chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 1, world } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 2, 1 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 2, world } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 2, world } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 3, 1 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 4, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 4, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 4, world } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 5, 1 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 5, world } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, 5 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, world } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 6, world } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 7, 5 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 7, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, 7 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, 6 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 },{ 8, world } } + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, bondCount, c_bonds }; + + // create asset + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_WARNING; + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + // split in 2 + std::vector actors; + { + NvBlastExtRadialDamageDesc damage = { + 10.0f, // compressive + { 0.0f, 0.0f, 0.0f }, // position + 100.0f, // min radius - maximum damage + 100.0f // max radius - zero damage + }; + + NvBlastBondFractureData outBondFracture[bondCount]; + NvBlastChunkFractureData outChunkFracture[chunksCount]; + + NvBlastFractureBuffers events; + events.bondFractureCount = 2; + events.bondFractures = outBondFracture; + events.chunkFractureCount = 2; + events.chunkFractures = outChunkFracture; + + NvBlastExtProgramParams programParams = { &damage, nullptr }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + NvBlastExtFalloffSubgraphShader + }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, myLog, nullptr); + NvBlastActorApplyFracture(nullptr, actor, &events, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(8, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + + actors.insert(actors.begin(), result.newActors, result.newActors + newActorsCount); + } + + for (NvBlastActor* actor : actors) + { + NvBlastActorDeactivate(actor, myLog); + } + + alignedFree(family); + alignedFree(asset); + + EXPECT_NO_WARNING; +} + +#if 0 +TEST(APITest, UserChunkMap) +{ + for (int i = 0; i < 2; ++i) + { + // Choose descriptor list + const NvBlastAssetDesc* descs = nullptr; + size_t size = 0; + switch (i) + { + case 0: + descs = g_assetDescs; + size = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + break; + case 1: + descs = g_assetDescsMissingCoverage; + size = sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); + break; + default: + continue; + } + + // Iterate over list + for (size_t j = 0; j < size; ++j) + { + // Create asset + const NvBlastAssetDesc* desc = descs + j; + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(desc)); + std::vector chunkMap(desc->chunkCount); + NvBlastAsset* asset = NvBlastCreateAsset(&chunkMap[0], desc, alignedAlloc, scratch.data(), nullptr); + EXPECT_TRUE(asset); + + // Test map + Nv::Blast::Asset& a = static_cast(asset); + uint32_t supportChunkCount = 0; + uint32_t subsupportChunkCount = 0; + for (uint32_t i = 0; i < desc->chunkCount; ++i) + { + const uint32_t map = chunkMap[i]; + if (Nv::Blast::isInvalidIndex(map)) + { + continue; + } + else if (map < a.m_firstSubsupportChunkIndex) + { + EXPECT_LT(map, asset.m_graph.m_nodeCount); + ++supportChunkCount; + } + else + { + EXPECT_LT(map, asset.m_chunkCount); + EXPECT_GE(map, asset.m_graph.m_nodeCount); + ++subsupportChunkCount; + } + } + EXPECT_EQ(supportChunkCount, asset.m_graph.m_nodeCount); + EXPECT_EQ(subsupportChunkCount, a.getLowerSupportChunkCount() - asset.m_graph.m_nodeCount); + + // Release asset + NvBlastAssetRelease(asset, free, nullptr); + } + } +} +#endif + +TEST_F(APITest, NoBondsSausage) +{ + // create asset + const NvBlastChunkDesc c_chunks[4] = + { + // centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 2 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 3 } + }; + + NvBlastAssetDesc assetDesc; + assetDesc.chunkCount = 4; + assetDesc.chunkDescs = c_chunks; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + const NvBlastChunk* chunks = NvBlastAssetGetChunks(asset, messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // check visible chunk + { + EXPECT_EQ(NvBlastActorGetVisibleChunkCount(actor, messageLog), 1); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, messageLog); + EXPECT_EQ(chunks[chunkIndex].userData, 0); + } + + // damage + NvBlastExtRadialDamageDesc damage = { + 10.0f, // compressive + { 0.0f, 0.0f, 0.0f }, // position + 4.0f, // min radius - maximum damage + 6.0f // max radius - zero damage + }; + + NvBlastBondFractureData outBondFracture[2]; + NvBlastChunkFractureData outChunkFracture[2]; + + NvBlastFractureBuffers events; + events.bondFractureCount = 2; + events.bondFractures = outBondFracture; + events.chunkFractureCount = 2; + events.chunkFractures = outChunkFracture; + + NvBlastExtProgramParams programParams = { &damage, nullptr }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + NvBlastExtFalloffSubgraphShader + }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, messageLog, nullptr); + NvBlastActorApplyFracture(&events, actor, &events, messageLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + EXPECT_EQ(0, events.bondFractureCount); + EXPECT_EQ(1, events.chunkFractureCount); + + // split + NvBlastActor* newActors[8]; /* num lower-support chunks? plus space for deletedActor */ + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors; + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + size_t newActorsCount = NvBlastActorSplit(&result, actor, 8, scratch.data(), messageLog, nullptr); + EXPECT_EQ(1, newActorsCount); + EXPECT_EQ(true, result.deletedActor == actor); + + // check visible chunk + { + EXPECT_EQ(NvBlastActorGetVisibleChunkCount(result.newActors[0], messageLog), 1); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, result.newActors[0], messageLog); + EXPECT_EQ(chunks[chunkIndex].userData, 3); + } + + // release all + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + + alignedFree(family); + alignedFree(asset); +} + +TEST_F(APITest, SplitOnlyWhenNecessary) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[4] = + { + // normal, area, centroid, userdata, chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 3, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 }, { 1, 3 } } + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 4, c_bonds }; + + // create asset with chunk map + std::vector scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alignedZeroedAlloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alignedZeroedAlloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + + // damage health only (expect no split) + { + NvBlastBondFractureData command[] = + { + { 0, 0, 1, 0.99f }, + { 0, 1, 2, 0.50f }, + { 0, 2, 3, 0.01f } + }; + + NvBlastFractureBuffers commands = { 3, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + + EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); + EXPECT_EQ(0, chunkIndex); + } + + // break 1 bond (expect no split) + { + NvBlastBondFractureData command[] = + { + { 0, 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 1, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + + EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); + EXPECT_EQ(0, chunkIndex); + } + + // split in 4 + std::vector actors; + { + NvBlastBondFractureData command[] = + { + { 0, 0, 1, 10.0f }, + { 0, 1, 2, 10.0f }, + { 0, 2, 3, 10.0f } + }; + + NvBlastFractureBuffers commands = { 3, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_TRUE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(4, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + + actors.insert(actors.begin(), result.newActors, result.newActors + newActorsCount); + } + + // damage chunk's health only (expect no split) + { + for (NvBlastActor* actor : actors) + { + uint32_t chunkToDamage; + NvBlastActorGetVisibleChunkIndices(&chunkToDamage, 1, actor, myLog); + + NvBlastChunkFractureData command[] = + { + { 0, chunkToDamage, 0.9f }, + }; + + NvBlastFractureBuffers commands = { 0, 1, nullptr, command }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + EXPECT_FALSE(NvBlastActorIsSplitRequired(actor, messageLog)); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + + EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); + EXPECT_EQ(chunkToDamage, chunkIndex); + } + } + + for (NvBlastActor* actor : actors) + { + NvBlastActorDeactivate(actor, myLog); + } + + alignedFree(family); + alignedFree(asset); + + EXPECT_NO_WARNING; +} + +#if NV_WINDOWS_FAMILY +#include +TEST_F(APITest,CExportsNoNameMangling) +{ + + // + // tests the lib-link-free approach using unmangled names (extern "C") + // + +#if NV_WIN32 +#if NV_DEBUG + const char* dllName = "NvBlastDebug_x86.dll"; +#elif NV_CHECKED + const char* dllName = "NvBlastChecked_x86.dll"; +#elif NV_PROFILE + const char* dllName = "NvBlastProfile_x86.dll"; +#else + const char* dllName = "NvBlast_x86.dll"; +#endif +#elif NV_WIN64 +#if NV_DEBUG + const char* dllName = "NvBlastDebug_x64.dll"; +#elif NV_CHECKED + const char* dllName = "NvBlastChecked_x64.dll"; +#elif NV_PROFILE + const char* dllName = "NvBlastProfile_x64.dll"; +#else + const char* dllName = "NvBlast_x64.dll"; +#endif +#endif + + HMODULE dllHandle = LoadLibrary(TEXT(dllName)); + DWORD error = GetLastError(); + ASSERT_TRUE(dllHandle != nullptr); + + + // Asset functions + typedef size_t(*NvBlastGetRequiredScratchForCreateAsset)(const NvBlastAssetDesc* desc); + typedef size_t(*NvBlastGetAssetMemorySize)(const NvBlastAssetDesc* desc); + typedef NvBlastAsset*(*NvBlastCreateAsset)(void* mem, const NvBlastAssetDesc* desc, void* scratch, NvBlastLog logFn); + + NvBlastGetRequiredScratchForCreateAsset assetCreateRequiredScratch = (NvBlastGetRequiredScratchForCreateAsset)GetProcAddress(dllHandle, TEXT("NvBlastGetRequiredScratchForCreateAsset")); + ASSERT_TRUE(assetCreateRequiredScratch != nullptr); + + NvBlastGetAssetMemorySize assetGetMemorySize = (NvBlastGetAssetMemorySize)GetProcAddress(dllHandle, TEXT("NvBlastGetAssetMemorySize")); + ASSERT_TRUE(assetGetMemorySize != nullptr); + + NvBlastCreateAsset assetCreate = (NvBlastCreateAsset)GetProcAddress(dllHandle, TEXT("NvBlastCreateAsset")); + ASSERT_TRUE(assetCreate != nullptr); + + // Family functions + typedef NvBlastFamily* (*NvBlastAssetCreateFamily)(void* mem, const NvBlastAsset* asset, NvBlastLog logFn); + typedef size_t(*NVBLASTASSETGETFAMILYMEMORYSIZE)(const NvBlastAsset* asset); + + NVBLASTASSETGETFAMILYMEMORYSIZE familyGetMemorySize = (NVBLASTASSETGETFAMILYMEMORYSIZE)GetProcAddress(dllHandle, TEXT("NvBlastAssetGetFamilyMemorySize")); + ASSERT_TRUE(familyGetMemorySize != nullptr); + + NvBlastAssetCreateFamily familyCreate = (NvBlastAssetCreateFamily)GetProcAddress(dllHandle, TEXT("NvBlastAssetCreateFamily")); + ASSERT_TRUE(familyCreate != nullptr); + + + // Actor functions + typedef size_t(*NvBlastFamilyGetRequiredScratchForCreateFirstActor)(const NvBlastFamily* family); + typedef NvBlastActor* (*NvBlastFamilyCreateFirstActor)(NvBlastFamily* family, const NvBlastActorDesc* desc, void* scratch, NvBlastLog logFn); + typedef bool(*NVBLASTACTORDEACTIVATE)(NvBlastActor* actor); + + NvBlastFamilyGetRequiredScratchForCreateFirstActor actorcreaterequiredscratch = (NvBlastFamilyGetRequiredScratchForCreateFirstActor)GetProcAddress(dllHandle, TEXT("NvBlastFamilyGetRequiredScratchForCreateFirstActor")); + ASSERT_TRUE(actorcreaterequiredscratch != nullptr); + + NvBlastFamilyCreateFirstActor actorCreate = (NvBlastFamilyCreateFirstActor)GetProcAddress(dllHandle, TEXT("NvBlastFamilyCreateFirstActor")); + ASSERT_TRUE(actorCreate != nullptr); + + NVBLASTACTORDEACTIVATE actorRelease = (NVBLASTACTORDEACTIVATE)GetProcAddress(dllHandle, TEXT("NvBlastActorDeactivate")); + ASSERT_TRUE(actorRelease != nullptr); + + + const NvBlastChunkDesc c_chunks[] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, + }; + + NvBlastAssetDesc assetDesc; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.chunkCount = 4; + assetDesc.chunkDescs = c_chunks; + + NvBlastAsset* asset; + { + size_t requiredsize = assetCreateRequiredScratch(&assetDesc); + std::vectorscratch(requiredsize); + void* mem = alignedZeroedAlloc(assetGetMemorySize(&assetDesc)); + asset = assetCreate(mem, &assetDesc, scratch.data(), myLog); + ASSERT_TRUE(asset != nullptr); + } + + void* fmem = alignedZeroedAlloc(familyGetMemorySize(asset)); + NvBlastFamily* family = familyCreate(fmem, asset, myLog); + + { + NvBlastActorDesc actorD; + actorD.initialBondHealths = actorD.initialSupportChunkHealths = nullptr; + actorD.uniformInitialBondHealth = actorD.uniformInitialLowerSupportChunkHealth = 1.0f; + + size_t requiredsize = actorcreaterequiredscratch(family); + std::vectorscratch(requiredsize); + NvBlastActor* actor = actorCreate(family, &actorD, scratch.data(), myLog); + ASSERT_TRUE(actor != nullptr); + + ASSERT_TRUE(actorRelease(actor)); + } + + alignedFree(family); + alignedFree(asset); + + EXPECT_NO_WARNING; +} +#endif diff --git a/test/src/unit/ActorTests.cpp b/test/src/unit/ActorTests.cpp old mode 100644 new mode 100755 index d4de648..79bb1fc --- a/test/src/unit/ActorTests.cpp +++ b/test/src/unit/ActorTests.cpp @@ -1,1118 +1,1118 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBaseTest.h" -#include "AssetGenerator.h" - -#include -#include - -#include "NvBlastActor.h" -#include "NvBlastExtDamageShaders.h" - - -static bool chooseRandomGraphNodes(uint32_t* g, uint32_t count, const Nv::Blast::Actor& actor) -{ - const uint32_t graphNodeCount = actor.getGraphNodeCount(); - - if (graphNodeCount < count) - { - return false; - } - - std::vector graphNodeIndices(graphNodeCount); - uint32_t* index = graphNodeIndices.data(); - for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i ; ++i) - { - *index++ = (uint32_t)i; - } - struct UserDataSorter - { - UserDataSorter(const Nv::Blast::Actor& actor) : m_asset(*actor.getAsset()) {} - - bool operator () (uint32_t i0, uint32_t i1) const - { - const uint32_t c0 = m_asset.m_graph.getChunkIndices()[i0]; - const uint32_t c1 = m_asset.m_graph.getChunkIndices()[i1]; - if (Nv::Blast::isInvalidIndex(c0) || Nv::Blast::isInvalidIndex(c1)) - { - return c0 < c1; - } - return m_asset.getChunks()[c0].userData < m_asset.getChunks()[c1].userData; - } - - const Nv::Blast::Asset& m_asset; - } userDataSorter(actor); - std::sort(graphNodeIndices.data(), graphNodeIndices.data() + graphNodeCount, userDataSorter); - -#if 0 - std::vector descUserData(graphNodeCount); - for (uint32_t i = 0; i < graphNodeCount; ++i) - { - descUserData[i] = actor.getAsset()->m_chunks[actor.getAsset()->m_graph.m_chunkIndices[graphNodeIndices[i]]].userData; - } -#endif - - uint32_t t = 0; - uint32_t m = 0; - for (uint32_t i = 0; i < graphNodeCount && m < count; ++i, ++t) - { - NVBLAST_ASSERT(t < graphNodeCount); - if (t >= graphNodeCount) - { - break; - } - const float U = (float)rand()/RAND_MAX; // U is uniform random number in [0,1) - if ((graphNodeCount - t)*U < count - m) - { - g[m++] = graphNodeIndices[i]; - } - } - - return m == count; -} - - -static void blast(std::set& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) -{ - std::vector chunkEvents; /* num lower-support chunks + bonds */ - std::vector bondEvents; /* num lower-support chunks + bonds */ - chunkEvents.resize(testAsset->solverChunks.size()); - bondEvents.resize(testAsset->solverBonds.size()); - - - std::vector splitScratch; - std::vector newActorsBuffer(testAsset->solverChunks.size()); - - NvBlastExtRadialDamageDesc damage = { - compressiveDamage, - { localPos.x, localPos.y, localPos.z }, - minRadius, - maxRadius - }; - - NvBlastExtProgramParams programParams = - { - &damage, - nullptr - }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - nullptr - }; - - size_t totalNewActorsCount = 0; - for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) - { - NvBlastActor* actor = *k; - NvBlastFractureBuffers events = { static_cast(bondEvents.size()), static_cast(chunkEvents.size()), bondEvents.data(), chunkEvents.data() }; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr); - NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr); - const bool isDamaged = NvBlastActorIsSplitRequired(actor, nullptr); - bool removeActor = false; - - if (events.bondFractureCount + events.chunkFractureCount > 0) - { - NvBlastActorSplitEvent splitEvent; - splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount]; - uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount); - - splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); - const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr); - EXPECT_TRUE(isDamaged || newActorsCount == 0); - totalNewActorsCount += newActorsCount; - removeActor = splitEvent.deletedActor != NULL; - } - else - { - EXPECT_FALSE(isDamaged); - } - - if (removeActor) - { - k = actorsToDamage.erase(k); - } - else - { - ++k; - } - } - - for (size_t i = 0; i < totalNewActorsCount; ++i) - { - actorsToDamage.insert(newActorsBuffer[i]); - } -} - - -template -class ActorTest : public BlastBaseTest -{ -public: - ActorTest() - { - - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - BlastBaseTest::alignedFree(mem); - } - - NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc) - { - // fix desc if wrong order or missing coverage first - NvBlastAssetDesc fixedDesc = desc; - std::vector chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount); - std::vector bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount); - std::vector chunkReorderMap(desc.chunkCount); - std::vector scratch(desc.chunkCount * sizeof(NvBlastChunkDesc)); - NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog); - NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog); - fixedDesc.chunkDescs = chunkDescs.data(); - fixedDesc.bondDescs = bondDescs.empty() ? nullptr : bondDescs.data(); - - // create asset - m_scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&fixedDesc, messageLog)); - void* mem = alloc(NvBlastGetAssetMemorySize(&fixedDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, &fixedDesc, m_scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - return asset; - } - - void buildAssets() - { - m_assets.resize(getAssetDescCount()); - for (uint32_t i = 0; i < m_assets.size(); ++i) - { - m_assets[i] = buildAsset(g_assetDescs[i]); - } - } - - NvBlastActor* instanceActor(const NvBlastAsset& asset) - { - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(&asset, nullptr)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, &asset, nullptr); - std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - return actor; - } - - void instanceActors() - { - m_actors.resize(m_assets.size()); - for (uint32_t i = 0; i < m_actors.size(); ++i) - { - m_actors[i] = instanceActor(*m_assets[i]); - } - } - - void releaseActors() - { - for (uint32_t i = 0; i < m_actors.size(); ++i) - { - NvBlastFamily* family = NvBlastActorGetFamily(m_actors[i], messageLog); - - const bool actorReleaseResult = NvBlastActorDeactivate(m_actors[i], messageLog); - EXPECT_TRUE(actorReleaseResult); - - free(family); - } - } - - void destroyAssets() - { - for (uint32_t i = 0; i < m_assets.size(); ++i) - { - free(m_assets[i]); - } - } - - void instanceAndPartitionRecursively - ( - const NvBlastAsset& asset, - bool partitionToSubsupport, - void (*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), - void (*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool) - ) - { - const Nv::Blast::Asset& solverAsset = *static_cast(&asset); - - std::vector actors; - std::vector buffer(NvBlastAssetGetChunkCount(&asset, messageLog)); - - // Instance the first actor from the asset - actors.push_back(static_cast(instanceActor(asset))); - - NvBlastFamily* family = NvBlastActorGetFamily(actors[0], messageLog); - - const uint32_t supportChunkCount = NvBlastAssetGetSupportChunkCount(&asset, messageLog); - const uint32_t leafChunkCount = actors[0]->getAsset()->m_leafChunkCount; - - // Now randomly partition the actors in the array, and keep going until we're down to single support chunks - bool canFracture = true; - - while (canFracture) - { - canFracture = false; - - for (uint32_t actorToPartition = 0; actorToPartition < actors.size(); ++actorToPartition) - { - Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[actorToPartition]; - if (a == nullptr) - { - continue; - } - - m_scratch.reserve((size_t)NvBlastActorGetRequiredScratchForSplit(a, messageLog)); - - if (preSplitTest) - { - preSplitTest(*a, nullptr); - } - - const bool singleLowerSupportChunk = a->getGraphNodeCount() <= 1; - uint32_t newActorCount = 0; - - for (int damageNum = 0; newActorCount < 2 && damageNum < 100; ++damageNum) // Avoid infinite loops - { - if (!singleLowerSupportChunk) - { - uint32_t g[2]; - chooseRandomGraphNodes(g, 2, *a); - const uint32_t bondIndex = solverAsset.m_graph.findBond(g[0], g[1]); - if (bondIndex != Nv::Blast::invalidIndex()) - { - a->damageBond(g[0], g[1], bondIndex, 100.0f); - a->findIslands(m_scratch.data()); - } - } - else - if (!partitionToSubsupport) - { - continue; - } - - // Split actor - newActorCount = a->partition((Nv::Blast::Actor**)&buffer[0], (uint32_t)buffer.size(), messageLog); - - if (newActorCount >= 2) - { - actors[actorToPartition] = nullptr; - } - } - - if (newActorCount > 1) - { - canFracture = true; - } - - for (uint32_t i = 0; i < newActorCount; ++i) - { - actors.push_back(buffer[i]); - buffer[i]->updateVisibleChunksFromGraphNodes(); - } - } - } - - if (postSplitTest) - { - postSplitTest(actors, leafChunkCount, supportChunkCount, partitionToSubsupport); - } - - for (auto actor : actors) - { - if (actor) - actor->release(); - } - - free(family); - } - - static void recursivePartitionPostSplitTestCounts(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) - { - // Test to see that all actors are split down to single support chunks - uint32_t remainingActorCount = 0; - for (uint32_t i = 0; i < actors.size(); ++i) - { - Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; - if (a == nullptr) - { - continue; - } - - ++remainingActorCount; - - NVBLAST_ASSERT(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); - EXPECT_TRUE(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); - if (!partitionToSubsupport) - { - EXPECT_EQ(1, a->getGraphNodeCount()); - } - - if (0 == a->getVisibleChunkCount()) - { - EXPECT_TRUE(a->isBoundToWorld()); - EXPECT_EQ(1, a->getGraphNodeCount()); - EXPECT_EQ(a->getFamilyHeader()->m_asset->m_graph.m_nodeCount - 1, a->getFirstGraphNodeIndex()); - --remainingActorCount; // Do not count this as a remaining actor, to be compared with leaf or support chunk counts later - } - - const bool actorReleaseResult = NvBlastActorDeactivate(actors[i], nullptr); - EXPECT_TRUE(actorReleaseResult); - } - - if (partitionToSubsupport) - { - EXPECT_EQ(leafChunkCount, remainingActorCount); - } - else - { - EXPECT_EQ(supportChunkCount, remainingActorCount); - } - } - - static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog) - { - const Nv::Blast::Asset& asset = *actor.getAsset(); - const NvBlastChunk* chunks = asset.getChunks(); - - if (actor.isSubSupportChunk()) - { - EXPECT_EQ(1, actor.getVisibleChunkCount()); - - const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor); - - EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex); - - // Make sure the visible chunk is subsupport - // Array of support flags - std::vector isSupport(asset.m_chunkCount, false); - for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i) - { - const uint32_t chunkIndex = asset.m_graph.getChunkIndices()[i]; - if (!Nv::Blast::isInvalidIndex(chunkIndex)) - { - isSupport[chunkIndex] = true; - } - } - - // Climb hierarchy to find support chunk - uint32_t chunkIndex = firstVisibleChunkIndex; - while (chunkIndex != Nv::Blast::invalidIndex()) - { - if (isSupport[chunkIndex]) - { - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - - EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); - } - else - { - // Array of visibility flags - std::vector isVisible(asset.m_chunkCount, false); - for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i) - { - isVisible[(uint32_t)i] = true; - } - - // Mark visible nodes representing graph chunks - std::vector visibleChunkFound(asset.m_chunkCount, false); - - // Make sure every graph chunk is represented by a visible chunk, or represents the world - for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i) - { - const uint32_t graphNodeIndex = (uint32_t)i; - uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex]; - // Climb hierarchy to find visible chunk - while (chunkIndex != Nv::Blast::invalidIndex()) - { - // Check that chunk owners are accurate - EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]); - if (isVisible[chunkIndex]) - { - visibleChunkFound[chunkIndex] = true; - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - EXPECT_TRUE(!Nv::Blast::isInvalidIndex(chunkIndex) || (graphNodeIndex == asset.m_graph.m_nodeCount-1 && actor.isBoundToWorld())); - } - - // Check that all visible chunks are accounted for - for (uint32_t i = 0; i < asset.m_chunkCount; ++i) - { - EXPECT_EQ(visibleChunkFound[i], isVisible[i]); - } - - // Make sure that, if all siblings are intact, they are invisible - for (uint32_t i = 0; i < asset.m_chunkCount; ++i) - { - bool allIntact = true; - bool noneVisible = true; - if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport - { - for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j) - { - allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex(); - noneVisible = noneVisible && !isVisible[j]; - } - EXPECT_TRUE(!allIntact || noneVisible); - } - } - } - } - - static void recursivePartitionPostSplitTestVisibleChunks(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) - { - for (uint32_t i = 0; i < actors.size(); ++i) - { - Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; - if (a == nullptr) - { - continue; - } - - testActorVisibleChunks(*a, nullptr); - } - } - - void partitionActorsToSupportChunks - ( - uint32_t assetDescCount, - const NvBlastAssetDesc* assetDescs, - void(*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), - void(*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool), - bool partitionToSubsupport - ) - { - srand(0); - - for (uint32_t i = 0; i < assetDescCount; ++i) - { - // Create an asset - NvBlastAsset* asset = buildAsset(assetDescs[i]); - - // Perform repeated partitioning - instanceAndPartitionRecursively(*asset, partitionToSubsupport, preSplitTest, postSplitTest); - - // Free the asset - free(asset); - } - } - - static void compareFamilies(const NvBlastFamily* family1, const NvBlastFamily* family2, size_t size, NvBlastLog logFn) - { - const char* block1 = reinterpret_cast(family1); - const char* block2 = reinterpret_cast(family2); -#if 0 - EXPECT_EQ(0, memcmp(block1, block2, size)); -#else - bool diffFound = false; - size_t startDiff = 0; - for (size_t i = 0; i < size; ++i) - { - if (block1[i] != block2[i]) - { - diffFound = true; - startDiff = i; - break; - } - } - if (!diffFound) - { - return; - } - size_t endDiff = startDiff; - for (size_t i = size; i--;) - { - if (block1[i] != block2[i]) - { - endDiff = i; - break; - } - } - std::ostringstream msg; - msg << "Block deserialization does not match current block in position range [" << startDiff << ", " << endDiff << "]."; - logFn(NvBlastMessage::Error, msg.str().c_str(), __FILE__, __LINE__); -#endif - } - - static void testActorBlockSerialize(std::vector& actors, NvBlastLog logFn) - { - if (actors.size()) - { - const NvBlastFamily* family = NvBlastActorGetFamily(actors[0], logFn); - const uint32_t size = NvBlastFamilyGetSize(family, logFn); - s_storage.insert(s_storage.end(), (char*)family, (char*)family + size); - } - } - - static void testActorBlockDeserialize(std::vector& actors, NvBlastLog logFn) - { - if (actors.size()) - { - EXPECT_LT(s_curr, s_storage.size()); - const NvBlastFamily* family = reinterpret_cast(&s_storage[s_curr]); - const uint32_t size = NvBlastFamilyGetSize(family, logFn); - EXPECT_LE(s_curr + size, s_storage.size()); - s_curr += size; - const NvBlastFamily* actorFamily = NvBlastActorGetFamily(actors[0], logFn); - // Family may contain different assets pointers, copy into new family block and set the same asset before comparing - Nv::Blast::Actor& a = *static_cast(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - std::vector storageFamilyCopy((char*)family, (char*)family + size); - NvBlastFamily* storageFamily = reinterpret_cast(storageFamilyCopy.data()); - NvBlastFamilySetAsset(storageFamily, solverAsset, logFn); - { - const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn); - std::vector blockActors(actorCountExpected); - const uint32_t actorCountReturned = NvBlastFamilyGetActors(blockActors.data(), actorCountExpected, storageFamily, logFn); - EXPECT_EQ(actorCountExpected, actorCountReturned); - } - compareFamilies(storageFamily, actorFamily, size, logFn); - } - } - - // Serialize all actors and then deserialize back into a new family in a random order, and compare with the original family - static void testActorSerializationNewFamily(std::vector& actors, NvBlastLog logFn) - { - if (actors.size() == 0) - { - return; - } - - Nv::Blast::Actor& a = *static_cast(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - - const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); - - std::vector< std::vector > streams(actors.size()); - for (size_t i = 0; i < actors.size(); ++i) - { - const uint32_t serSize = NvBlastActorGetSerializationSize(actors[i], logFn); - EXPECT_GE(serSizeBound, serSize); - std::vector& stream = streams[i]; - stream.resize(serSize); - const uint32_t bytesWritten = NvBlastActorSerialize(stream.data(), serSize, actors[i], logFn); - EXPECT_EQ(serSize, bytesWritten); - } - - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(solverAsset, logFn)); - NvBlastFamily* newFamily = NvBlastAssetCreateFamily(fmem, solverAsset, logFn); - - std::vector order(actors.size()); - for (size_t i = 0; i < order.size(); ++i) - { - order[i] = i; - } - std::random_shuffle(order.begin(), order.end()); - - for (size_t i = 0; i < actors.size(); ++i) - { - NvBlastActor* newActor = NvBlastFamilyDeserializeActor(newFamily, streams[order[i]].data(), logFn); - EXPECT_TRUE(newActor != nullptr); - } - - const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); - compareFamilies(oldFamily, newFamily, NvBlastFamilyGetSize(oldFamily, logFn), logFn); - - free(newFamily); - } - - // Copy the family and then serialize some subset of actors, deleting them afterwards. - // Then, deserialize back into the block and compare the original and new families. - static void testActorSerializationPartialBlock(std::vector& actors, NvBlastLog logFn) - { - if (actors.size() <= 1) - { - return; - } - - Nv::Blast::Actor& a = *static_cast(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - - const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); - const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn); - std::vector buffer((char*)oldFamily, (char*)oldFamily + size); - NvBlastFamily* familyCopy = reinterpret_cast(buffer.data()); - - const uint32_t serCount = 1 + (rand() % actors.size() - 1); - - const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn); - std::vector actorsRemaining(actorCount); - const uint32_t actorsInFamily = NvBlastFamilyGetActors(&actorsRemaining[0], actorCount, familyCopy, logFn); - EXPECT_EQ(actorCount, actorsInFamily); - - const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); - - std::vector< std::vector > streams(serCount); - for (uint32_t i = 0; i < serCount; ++i) - { - std::vector& stream = streams[i]; - const uint32_t indexToStream = rand() % actorsRemaining.size(); - NvBlastActor* actorToStream = actorsRemaining[indexToStream]; - std::swap(actorsRemaining[indexToStream], actorsRemaining[actorsRemaining.size() - 1]); - actorsRemaining.pop_back(); - const uint32_t serSize = NvBlastActorGetSerializationSize(actorToStream, logFn); - EXPECT_GE(serSizeBound, serSize); - stream.resize(serSize); - const uint32_t bytesWritten = NvBlastActorSerialize(&stream[0], serSize, actorToStream, logFn); - EXPECT_EQ(serSize, bytesWritten); - NvBlastActorDeactivate(actorToStream, logFn); - } - - for (uint32_t i = 0; i < serCount; ++i) - { - NvBlastActor* newActor = NvBlastFamilyDeserializeActor(familyCopy, streams[i].data(), logFn); - EXPECT_TRUE(newActor != nullptr); - } - - compareFamilies(oldFamily, familyCopy, size, logFn); - } - - void damageLeafSupportActors - ( - uint32_t assetCount, - uint32_t familyCount, - uint32_t damageCount, - bool simple, - void (*actorTest)(const Nv::Blast::Actor&, NvBlastLog), - void (*postDamageTest)(std::vector&, NvBlastLog), - CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::BondFlags::ALL_INTERNAL_BONDS - ) - { - const float relativeDamageRadius = simple ? 0.75f : 0.2f; - const float compressiveDamage = 1.0f; - const uint32_t minChunkCount = simple ? 9 : 100; - const uint32_t maxChunkCount = simple ? 9 : 10000; - const bool printActorCount = false; - - srand(0); - - std::cout << "Asset # (out of " << assetCount << "): "; - for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) - { - std::cout << assetNum + 1 << ".. "; - CubeAssetGenerator::Settings settings; - settings.extents = GeneratorAsset::Vec3(1, 1, 1); - settings.bondFlags = bondFlags; - CubeAssetGenerator::DepthInfo depthInfo; - depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); - depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; - settings.depths.push_back(depthInfo); - uint32_t chunkCount = 1; - while (chunkCount < minChunkCount) - { - uint32_t chunkMul; - do - { - depthInfo.slicesPerAxis = simple ? GeneratorAsset::Vec3(2, 2, 2) : GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); - chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); - } while (chunkMul == 1); - if (chunkCount*chunkMul > maxChunkCount) - { - break; - } - chunkCount *= chunkMul; - settings.depths.push_back(depthInfo); - settings.extents = settings.extents * depthInfo.slicesPerAxis; - } - settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support - - // Make largest direction unit size - settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); - - // Create asset - GeneratorAsset testAsset; - CubeAssetGenerator::generate(testAsset, settings); - - NvBlastAssetDesc desc; - desc.chunkDescs = testAsset.solverChunks.data(); - desc.chunkCount = (uint32_t)testAsset.solverChunks.size(); - desc.bondDescs = testAsset.solverBonds.data(); - desc.bondCount = (uint32_t)testAsset.solverBonds.size(); - NvBlastAsset* asset = buildAsset(desc); - NvBlastID assetID = NvBlastAssetGetID(asset, messageLog); - - // copy asset (for setAsset testing) - const char* data = (const char*)asset; - const uint32_t dataSize = NvBlastAssetGetSize(asset, messageLog); - char* duplicateData = (char*)alloc(dataSize); - memcpy(duplicateData, data, dataSize); - NvBlastAsset* assetDuplicate = (NvBlastAsset*)duplicateData; - - // Generate families - for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) - { - // family - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); // Using zeroingAlloc in case actorTest compares memory blocks - NvBlastID id = NvBlastFamilyGetAssetID(family, messageLog); - EXPECT_TRUE(!memcmp(&assetID, &id, sizeof(NvBlastID))); - if (rand() % 2 == 0) - { - // replace asset with duplicate in half of cases to test setAsset - NvBlastFamilySetAsset(family, assetDuplicate, messageLog); - NvBlastID id2 = NvBlastFamilyGetAssetID(family, messageLog); - EXPECT_TRUE(!memcmp(&assetID, &id2, sizeof(NvBlastID))); - } - - // actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - m_scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, m_scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // Generate damage - std::set actors; - actors.insert(actor); - if (printActorCount) std::cout << "Actors: 1.. "; - for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) - { - GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); - blast(actors, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage); - if (printActorCount) std::cout << actors.size() << ".. "; - if (actors.size() > 0) - { - const NvBlastFamily* family = NvBlastActorGetFamily(*actors.begin(), messageLog); - const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog); - EXPECT_EQ((uint32_t)actors.size(), actorCount); - if ((uint32_t)actors.size() == actorCount) - { - std::vector buffer1(actorCount); - const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog); - EXPECT_EQ(actorsWritten, actorCount); - std::vector buffer2(actors.begin(), actors.end()); - EXPECT_EQ(0, memcmp(&buffer1[0], buffer2.data(), actorCount*sizeof(NvBlastActor*))); - } - } - // Test individual actors - if (actorTest != nullptr) - { - for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) - { - actorTest(*static_cast(*k), messageLog); - } - } - } - if (printActorCount) std::cout << "\n"; - - // Test fractured actor set - if (postDamageTest) - { - std::vector actorArray(actors.begin(), actors.end()); - postDamageTest(actorArray, messageLog); - } - - // Release remaining actors - for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) - { - NvBlastActorDeactivate(*k, messageLog); - } - actors.clear(); - - free(family); - } - - // Release asset data - free(asset); - free(assetDuplicate); - } - std::cout << "done.\n"; - } - - std::vector m_assets; - std::vector m_actors; - std::vector m_scratch; - static std::vector s_storage; - - static size_t s_curr; -}; - -// Static values -template -std::vector ActorTest::s_storage; - -template -size_t ActorTest::s_curr; - -// Specializations -typedef ActorTest ActorTestAllowWarnings; -typedef ActorTest ActorTestStrict; - -// Tests -TEST_F(ActorTestStrict, InstanceActors) -{ - // Build assets and instance actors - buildAssets(); - instanceActors(); - - // Release actors and destroy assets - releaseActors(); - destroyAssets(); -} - -TEST_F(ActorTestAllowWarnings, ActorHealthInitialization) -{ - // Test all assets - std::vector assetDescs; - assetDescs.insert(assetDescs.end(), g_assetDescs, g_assetDescs + getAssetDescCount()); - assetDescs.insert(assetDescs.end(), g_assetDescsMissingCoverage, g_assetDescsMissingCoverage + getAssetDescMissingCoverageCount()); - - struct TestMode - { - enum Enum - { - Uniform, - Nonuniform, - - Count - }; - }; - - for (auto assetDesc : assetDescs) - { - NvBlastAsset* asset = buildAsset(assetDesc); - EXPECT_TRUE(asset != nullptr); - - Nv::Blast::Asset& assetInt = static_cast(*asset); - - NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr); - - std::vector supportChunkHealths(graph.nodeCount); - for (size_t i = 0; i < supportChunkHealths.size(); ++i) - { - supportChunkHealths[i] = 1.0f + (float)i; - } - - std::vector bondHealths(assetInt.getBondCount()); - for (size_t i = 0; i < bondHealths.size(); ++i) - { - bondHealths[i] = 1.5f + (float)i; - } - - for (int chunkTestMode = 0; chunkTestMode < TestMode::Count; ++chunkTestMode) - { - for (int bondTestMode = 0; bondTestMode < TestMode::Count; ++bondTestMode) - { - NvBlastActorDesc actorDesc; - - switch (chunkTestMode) - { - default: - case TestMode::Uniform: - actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - break; - case TestMode::Nonuniform: - actorDesc.initialSupportChunkHealths = supportChunkHealths.data(); - break; - } - - switch (bondTestMode) - { - default: - case TestMode::Uniform: - actorDesc.initialBondHealths = nullptr; - actorDesc.uniformInitialBondHealth = 2.0f; - break; - case TestMode::Nonuniform: - actorDesc.initialBondHealths = bondHealths.data(); - break; - } - - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); - std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - Nv::Blast::Actor& actorInt = static_cast(*actor); - Nv::Blast::FamilyHeader* header = actorInt.getFamilyHeader(); - - - for (uint32_t i = 0; i < graph.nodeCount; ++i) - { - const uint32_t supportChunkIndex = graph.chunkIndices[i]; - for (Nv::Blast::Asset::DepthFirstIt it(assetInt, supportChunkIndex); (bool)it; ++it) - { - const uint32_t chunkIndex = (uint32_t)it; - const uint32_t lowerSupportIndex = assetInt.getContiguousLowerSupportIndex(chunkIndex); - NVBLAST_ASSERT(lowerSupportIndex < assetInt.getLowerSupportChunkCount()); - const float health = header->getLowerSupportChunkHealths()[lowerSupportIndex]; - switch (chunkTestMode) - { - default: - case TestMode::Uniform: - EXPECT_EQ(1.0f, health); - break; - case TestMode::Nonuniform: - EXPECT_EQ(supportChunkHealths[i], health); - break; - } - } - } - - for (uint32_t i = 0; i < assetInt.getBondCount(); ++i) - { - switch (bondTestMode) - { - default: - case TestMode::Uniform: - EXPECT_EQ(2.0f, header->getBondHealths()[i]); - break; - case TestMode::Nonuniform: - EXPECT_EQ(bondHealths[i], header->getBondHealths()[i]); - break; - } - } - - NvBlastActorDeactivate(actor, messageLog); - free(family); - } - } - - free(asset); - } -} - -TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, false); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, false); -} - -TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, true); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, true); -} - -TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); -} - -TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorsTestVisibility) -{ - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorTestBlockSerialization) -{ - s_storage.resize(0); - damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockSerialize); - s_curr = 0; - damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockDeserialize); - s_storage.resize(0); -} - -TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationNewFamily) -{ - damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationNewFamily); -} - -TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationPartialBlock) -{ - damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationPartialBlock); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationNewFamily) -{ - damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationNewFamily); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationPartialBlock) -{ - damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationPartialBlock); -} - -TEST_F(ActorTestStrict, DamageMultipleIslandLeafSupportActorsTestVisibility) -{ - typedef CubeAssetGenerator::BondFlags BF; - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Y_BONDS | BF::Z_BONDS); // Only connect y-z plane islands - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Z_BONDS); // Only connect z-direction islands - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::NO_BONDS); // All support chunks disconnected (single-chunk islands) -} - -TEST_F(ActorTestStrict, DamageBoundToWorldLeafSupportActorsTestVisibility) -{ - typedef CubeAssetGenerator::BondFlags BF; - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_MINUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Y_PLUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Z_MINUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::X_MINUS_WORLD_BONDS - | BF::Y_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS - | BF::Z_PLUS_WORLD_BONDS | BF::Z_MINUS_WORLD_BONDS); -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "BlastBaseTest.h" +#include "AssetGenerator.h" + +#include +#include + +#include "NvBlastActor.h" +#include "NvBlastExtDamageShaders.h" + + +static bool chooseRandomGraphNodes(uint32_t* g, uint32_t count, const Nv::Blast::Actor& actor) +{ + const uint32_t graphNodeCount = actor.getGraphNodeCount(); + + if (graphNodeCount < count) + { + return false; + } + + std::vector graphNodeIndices(graphNodeCount); + uint32_t* index = graphNodeIndices.data(); + for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i ; ++i) + { + *index++ = (uint32_t)i; + } + struct UserDataSorter + { + UserDataSorter(const Nv::Blast::Actor& actor) : m_asset(*actor.getAsset()) {} + + bool operator () (uint32_t i0, uint32_t i1) const + { + const uint32_t c0 = m_asset.m_graph.getChunkIndices()[i0]; + const uint32_t c1 = m_asset.m_graph.getChunkIndices()[i1]; + if (Nv::Blast::isInvalidIndex(c0) || Nv::Blast::isInvalidIndex(c1)) + { + return c0 < c1; + } + return m_asset.getChunks()[c0].userData < m_asset.getChunks()[c1].userData; + } + + const Nv::Blast::Asset& m_asset; + } userDataSorter(actor); + std::sort(graphNodeIndices.data(), graphNodeIndices.data() + graphNodeCount, userDataSorter); + +#if 0 + std::vector descUserData(graphNodeCount); + for (uint32_t i = 0; i < graphNodeCount; ++i) + { + descUserData[i] = actor.getAsset()->m_chunks[actor.getAsset()->m_graph.m_chunkIndices[graphNodeIndices[i]]].userData; + } +#endif + + uint32_t t = 0; + uint32_t m = 0; + for (uint32_t i = 0; i < graphNodeCount && m < count; ++i, ++t) + { + NVBLAST_ASSERT(t < graphNodeCount); + if (t >= graphNodeCount) + { + break; + } + const float U = (float)rand()/RAND_MAX; // U is uniform random number in [0,1) + if ((graphNodeCount - t)*U < count - m) + { + g[m++] = graphNodeIndices[i]; + } + } + + return m == count; +} + + +static void blast(std::set& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) +{ + std::vector chunkEvents; /* num lower-support chunks + bonds */ + std::vector bondEvents; /* num lower-support chunks + bonds */ + chunkEvents.resize(testAsset->solverChunks.size()); + bondEvents.resize(testAsset->solverBonds.size()); + + + std::vector splitScratch; + std::vector newActorsBuffer(testAsset->solverChunks.size()); + + NvBlastExtRadialDamageDesc damage = { + compressiveDamage, + { localPos.x, localPos.y, localPos.z }, + minRadius, + maxRadius + }; + + NvBlastExtProgramParams programParams = + { + &damage, + nullptr + }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + size_t totalNewActorsCount = 0; + for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) + { + NvBlastActor* actor = *k; + NvBlastFractureBuffers events = { static_cast(bondEvents.size()), static_cast(chunkEvents.size()), bondEvents.data(), chunkEvents.data() }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr); + NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr); + const bool isDamaged = NvBlastActorIsSplitRequired(actor, nullptr); + bool removeActor = false; + + if (events.bondFractureCount + events.chunkFractureCount > 0) + { + NvBlastActorSplitEvent splitEvent; + splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount]; + uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount); + + splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); + const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr); + EXPECT_TRUE(isDamaged || newActorsCount == 0); + totalNewActorsCount += newActorsCount; + removeActor = splitEvent.deletedActor != NULL; + } + else + { + EXPECT_FALSE(isDamaged); + } + + if (removeActor) + { + k = actorsToDamage.erase(k); + } + else + { + ++k; + } + } + + for (size_t i = 0; i < totalNewActorsCount; ++i) + { + actorsToDamage.insert(newActorsBuffer[i]); + } +} + + +template +class ActorTest : public BlastBaseTest +{ +public: + ActorTest() + { + + } + + static void messageLog(int type, const char* msg, const char* file, int line) + { + BlastBaseTest::messageLog(type, msg, file, line); + } + + static void* alloc(size_t size) + { + return BlastBaseTest::alignedZeroedAlloc(size); + } + + static void free(void* mem) + { + BlastBaseTest::alignedFree(mem); + } + + NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc) + { + // fix desc if wrong order or missing coverage first + NvBlastAssetDesc fixedDesc = desc; + std::vector chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount); + std::vector bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount); + std::vector chunkReorderMap(desc.chunkCount); + std::vector scratch(desc.chunkCount * sizeof(NvBlastChunkDesc)); + NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog); + NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog); + fixedDesc.chunkDescs = chunkDescs.data(); + fixedDesc.bondDescs = bondDescs.empty() ? nullptr : bondDescs.data(); + + // create asset + m_scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&fixedDesc, messageLog)); + void* mem = alloc(NvBlastGetAssetMemorySize(&fixedDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &fixedDesc, m_scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + return asset; + } + + void buildAssets() + { + m_assets.resize(getAssetDescCount()); + for (uint32_t i = 0; i < m_assets.size(); ++i) + { + m_assets[i] = buildAsset(g_assetDescs[i]); + } + } + + NvBlastActor* instanceActor(const NvBlastAsset& asset) + { + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(&asset, nullptr)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, &asset, nullptr); + std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + return actor; + } + + void instanceActors() + { + m_actors.resize(m_assets.size()); + for (uint32_t i = 0; i < m_actors.size(); ++i) + { + m_actors[i] = instanceActor(*m_assets[i]); + } + } + + void releaseActors() + { + for (uint32_t i = 0; i < m_actors.size(); ++i) + { + NvBlastFamily* family = NvBlastActorGetFamily(m_actors[i], messageLog); + + const bool actorReleaseResult = NvBlastActorDeactivate(m_actors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + + free(family); + } + } + + void destroyAssets() + { + for (uint32_t i = 0; i < m_assets.size(); ++i) + { + free(m_assets[i]); + } + } + + void instanceAndPartitionRecursively + ( + const NvBlastAsset& asset, + bool partitionToSubsupport, + void (*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), + void (*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool) + ) + { + const Nv::Blast::Asset& solverAsset = *static_cast(&asset); + + std::vector actors; + std::vector buffer(NvBlastAssetGetChunkCount(&asset, messageLog)); + + // Instance the first actor from the asset + actors.push_back(static_cast(instanceActor(asset))); + + NvBlastFamily* family = NvBlastActorGetFamily(actors[0], messageLog); + + const uint32_t supportChunkCount = NvBlastAssetGetSupportChunkCount(&asset, messageLog); + const uint32_t leafChunkCount = actors[0]->getAsset()->m_leafChunkCount; + + // Now randomly partition the actors in the array, and keep going until we're down to single support chunks + bool canFracture = true; + + while (canFracture) + { + canFracture = false; + + for (uint32_t actorToPartition = 0; actorToPartition < actors.size(); ++actorToPartition) + { + Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[actorToPartition]; + if (a == nullptr) + { + continue; + } + + m_scratch.reserve((size_t)NvBlastActorGetRequiredScratchForSplit(a, messageLog)); + + if (preSplitTest) + { + preSplitTest(*a, nullptr); + } + + const bool singleLowerSupportChunk = a->getGraphNodeCount() <= 1; + uint32_t newActorCount = 0; + + for (int damageNum = 0; newActorCount < 2 && damageNum < 100; ++damageNum) // Avoid infinite loops + { + if (!singleLowerSupportChunk) + { + uint32_t g[2]; + chooseRandomGraphNodes(g, 2, *a); + const uint32_t bondIndex = solverAsset.m_graph.findBond(g[0], g[1]); + if (bondIndex != Nv::Blast::invalidIndex()) + { + a->damageBond(g[0], g[1], bondIndex, 100.0f); + a->findIslands(m_scratch.data()); + } + } + else + if (!partitionToSubsupport) + { + continue; + } + + // Split actor + newActorCount = a->partition((Nv::Blast::Actor**)&buffer[0], (uint32_t)buffer.size(), messageLog); + + if (newActorCount >= 2) + { + actors[actorToPartition] = nullptr; + } + } + + if (newActorCount > 1) + { + canFracture = true; + } + + for (uint32_t i = 0; i < newActorCount; ++i) + { + actors.push_back(buffer[i]); + buffer[i]->updateVisibleChunksFromGraphNodes(); + } + } + } + + if (postSplitTest) + { + postSplitTest(actors, leafChunkCount, supportChunkCount, partitionToSubsupport); + } + + for (auto actor : actors) + { + if (actor) + actor->release(); + } + + free(family); + } + + static void recursivePartitionPostSplitTestCounts(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) + { + // Test to see that all actors are split down to single support chunks + uint32_t remainingActorCount = 0; + for (uint32_t i = 0; i < actors.size(); ++i) + { + Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; + if (a == nullptr) + { + continue; + } + + ++remainingActorCount; + + NVBLAST_ASSERT(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); + EXPECT_TRUE(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); + if (!partitionToSubsupport) + { + EXPECT_EQ(1, a->getGraphNodeCount()); + } + + if (0 == a->getVisibleChunkCount()) + { + EXPECT_TRUE(a->isBoundToWorld()); + EXPECT_EQ(1, a->getGraphNodeCount()); + EXPECT_EQ(a->getFamilyHeader()->m_asset->m_graph.m_nodeCount - 1, a->getFirstGraphNodeIndex()); + --remainingActorCount; // Do not count this as a remaining actor, to be compared with leaf or support chunk counts later + } + + const bool actorReleaseResult = NvBlastActorDeactivate(actors[i], nullptr); + EXPECT_TRUE(actorReleaseResult); + } + + if (partitionToSubsupport) + { + EXPECT_EQ(leafChunkCount, remainingActorCount); + } + else + { + EXPECT_EQ(supportChunkCount, remainingActorCount); + } + } + + static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog) + { + const Nv::Blast::Asset& asset = *actor.getAsset(); + const NvBlastChunk* chunks = asset.getChunks(); + + if (actor.isSubSupportChunk()) + { + EXPECT_EQ(1, actor.getVisibleChunkCount()); + + const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor); + + EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex); + + // Make sure the visible chunk is subsupport + // Array of support flags + std::vector isSupport(asset.m_chunkCount, false); + for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i) + { + const uint32_t chunkIndex = asset.m_graph.getChunkIndices()[i]; + if (!Nv::Blast::isInvalidIndex(chunkIndex)) + { + isSupport[chunkIndex] = true; + } + } + + // Climb hierarchy to find support chunk + uint32_t chunkIndex = firstVisibleChunkIndex; + while (chunkIndex != Nv::Blast::invalidIndex()) + { + if (isSupport[chunkIndex]) + { + break; + } + chunkIndex = chunks[chunkIndex].parentChunkIndex; + } + + EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); + } + else + { + // Array of visibility flags + std::vector isVisible(asset.m_chunkCount, false); + for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i) + { + isVisible[(uint32_t)i] = true; + } + + // Mark visible nodes representing graph chunks + std::vector visibleChunkFound(asset.m_chunkCount, false); + + // Make sure every graph chunk is represented by a visible chunk, or represents the world + for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i) + { + const uint32_t graphNodeIndex = (uint32_t)i; + uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex]; + // Climb hierarchy to find visible chunk + while (chunkIndex != Nv::Blast::invalidIndex()) + { + // Check that chunk owners are accurate + EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]); + if (isVisible[chunkIndex]) + { + visibleChunkFound[chunkIndex] = true; + break; + } + chunkIndex = chunks[chunkIndex].parentChunkIndex; + } + EXPECT_TRUE(!Nv::Blast::isInvalidIndex(chunkIndex) || (graphNodeIndex == asset.m_graph.m_nodeCount-1 && actor.isBoundToWorld())); + } + + // Check that all visible chunks are accounted for + for (uint32_t i = 0; i < asset.m_chunkCount; ++i) + { + EXPECT_EQ(visibleChunkFound[i], isVisible[i]); + } + + // Make sure that, if all siblings are intact, they are invisible + for (uint32_t i = 0; i < asset.m_chunkCount; ++i) + { + bool allIntact = true; + bool noneVisible = true; + if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport + { + for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j) + { + allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex(); + noneVisible = noneVisible && !isVisible[j]; + } + EXPECT_TRUE(!allIntact || noneVisible); + } + } + } + } + + static void recursivePartitionPostSplitTestVisibleChunks(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) + { + for (uint32_t i = 0; i < actors.size(); ++i) + { + Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; + if (a == nullptr) + { + continue; + } + + testActorVisibleChunks(*a, nullptr); + } + } + + void partitionActorsToSupportChunks + ( + uint32_t assetDescCount, + const NvBlastAssetDesc* assetDescs, + void(*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), + void(*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool), + bool partitionToSubsupport + ) + { + srand(0); + + for (uint32_t i = 0; i < assetDescCount; ++i) + { + // Create an asset + NvBlastAsset* asset = buildAsset(assetDescs[i]); + + // Perform repeated partitioning + instanceAndPartitionRecursively(*asset, partitionToSubsupport, preSplitTest, postSplitTest); + + // Free the asset + free(asset); + } + } + + static void compareFamilies(const NvBlastFamily* family1, const NvBlastFamily* family2, size_t size, NvBlastLog logFn) + { + const char* block1 = reinterpret_cast(family1); + const char* block2 = reinterpret_cast(family2); +#if 0 + EXPECT_EQ(0, memcmp(block1, block2, size)); +#else + bool diffFound = false; + size_t startDiff = 0; + for (size_t i = 0; i < size; ++i) + { + if (block1[i] != block2[i]) + { + diffFound = true; + startDiff = i; + break; + } + } + if (!diffFound) + { + return; + } + size_t endDiff = startDiff; + for (size_t i = size; i--;) + { + if (block1[i] != block2[i]) + { + endDiff = i; + break; + } + } + std::ostringstream msg; + msg << "Block deserialization does not match current block in position range [" << startDiff << ", " << endDiff << "]."; + logFn(NvBlastMessage::Error, msg.str().c_str(), __FILE__, __LINE__); +#endif + } + + static void testActorBlockSerialize(std::vector& actors, NvBlastLog logFn) + { + if (actors.size()) + { + const NvBlastFamily* family = NvBlastActorGetFamily(actors[0], logFn); + const uint32_t size = NvBlastFamilyGetSize(family, logFn); + s_storage.insert(s_storage.end(), (char*)family, (char*)family + size); + } + } + + static void testActorBlockDeserialize(std::vector& actors, NvBlastLog logFn) + { + if (actors.size()) + { + EXPECT_LT(s_curr, s_storage.size()); + const NvBlastFamily* family = reinterpret_cast(&s_storage[s_curr]); + const uint32_t size = NvBlastFamilyGetSize(family, logFn); + EXPECT_LE(s_curr + size, s_storage.size()); + s_curr += size; + const NvBlastFamily* actorFamily = NvBlastActorGetFamily(actors[0], logFn); + // Family may contain different assets pointers, copy into new family block and set the same asset before comparing + Nv::Blast::Actor& a = *static_cast(actors[0]); + const Nv::Blast::Asset* solverAsset = a.getAsset(); + std::vector storageFamilyCopy((char*)family, (char*)family + size); + NvBlastFamily* storageFamily = reinterpret_cast(storageFamilyCopy.data()); + NvBlastFamilySetAsset(storageFamily, solverAsset, logFn); + { + const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn); + std::vector blockActors(actorCountExpected); + const uint32_t actorCountReturned = NvBlastFamilyGetActors(blockActors.data(), actorCountExpected, storageFamily, logFn); + EXPECT_EQ(actorCountExpected, actorCountReturned); + } + compareFamilies(storageFamily, actorFamily, size, logFn); + } + } + + // Serialize all actors and then deserialize back into a new family in a random order, and compare with the original family + static void testActorSerializationNewFamily(std::vector& actors, NvBlastLog logFn) + { + if (actors.size() == 0) + { + return; + } + + Nv::Blast::Actor& a = *static_cast(actors[0]); + const Nv::Blast::Asset* solverAsset = a.getAsset(); + + const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); + + std::vector< std::vector > streams(actors.size()); + for (size_t i = 0; i < actors.size(); ++i) + { + const uint32_t serSize = NvBlastActorGetSerializationSize(actors[i], logFn); + EXPECT_GE(serSizeBound, serSize); + std::vector& stream = streams[i]; + stream.resize(serSize); + const uint32_t bytesWritten = NvBlastActorSerialize(stream.data(), serSize, actors[i], logFn); + EXPECT_EQ(serSize, bytesWritten); + } + + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(solverAsset, logFn)); + NvBlastFamily* newFamily = NvBlastAssetCreateFamily(fmem, solverAsset, logFn); + + std::vector order(actors.size()); + for (size_t i = 0; i < order.size(); ++i) + { + order[i] = i; + } + std::random_shuffle(order.begin(), order.end()); + + for (size_t i = 0; i < actors.size(); ++i) + { + NvBlastActor* newActor = NvBlastFamilyDeserializeActor(newFamily, streams[order[i]].data(), logFn); + EXPECT_TRUE(newActor != nullptr); + } + + const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); + compareFamilies(oldFamily, newFamily, NvBlastFamilyGetSize(oldFamily, logFn), logFn); + + free(newFamily); + } + + // Copy the family and then serialize some subset of actors, deleting them afterwards. + // Then, deserialize back into the block and compare the original and new families. + static void testActorSerializationPartialBlock(std::vector& actors, NvBlastLog logFn) + { + if (actors.size() <= 1) + { + return; + } + + Nv::Blast::Actor& a = *static_cast(actors[0]); + const Nv::Blast::Asset* solverAsset = a.getAsset(); + + const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); + const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn); + std::vector buffer((char*)oldFamily, (char*)oldFamily + size); + NvBlastFamily* familyCopy = reinterpret_cast(buffer.data()); + + const uint32_t serCount = 1 + (rand() % actors.size() - 1); + + const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn); + std::vector actorsRemaining(actorCount); + const uint32_t actorsInFamily = NvBlastFamilyGetActors(&actorsRemaining[0], actorCount, familyCopy, logFn); + EXPECT_EQ(actorCount, actorsInFamily); + + const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); + + std::vector< std::vector > streams(serCount); + for (uint32_t i = 0; i < serCount; ++i) + { + std::vector& stream = streams[i]; + const uint32_t indexToStream = rand() % actorsRemaining.size(); + NvBlastActor* actorToStream = actorsRemaining[indexToStream]; + std::swap(actorsRemaining[indexToStream], actorsRemaining[actorsRemaining.size() - 1]); + actorsRemaining.pop_back(); + const uint32_t serSize = NvBlastActorGetSerializationSize(actorToStream, logFn); + EXPECT_GE(serSizeBound, serSize); + stream.resize(serSize); + const uint32_t bytesWritten = NvBlastActorSerialize(&stream[0], serSize, actorToStream, logFn); + EXPECT_EQ(serSize, bytesWritten); + NvBlastActorDeactivate(actorToStream, logFn); + } + + for (uint32_t i = 0; i < serCount; ++i) + { + NvBlastActor* newActor = NvBlastFamilyDeserializeActor(familyCopy, streams[i].data(), logFn); + EXPECT_TRUE(newActor != nullptr); + } + + compareFamilies(oldFamily, familyCopy, size, logFn); + } + + void damageLeafSupportActors + ( + uint32_t assetCount, + uint32_t familyCount, + uint32_t damageCount, + bool simple, + void (*actorTest)(const Nv::Blast::Actor&, NvBlastLog), + void (*postDamageTest)(std::vector&, NvBlastLog), + CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::BondFlags::ALL_INTERNAL_BONDS + ) + { + const float relativeDamageRadius = simple ? 0.75f : 0.2f; + const float compressiveDamage = 1.0f; + const uint32_t minChunkCount = simple ? 9 : 100; + const uint32_t maxChunkCount = simple ? 9 : 10000; + const bool printActorCount = false; + + srand(0); + + std::cout << "Asset # (out of " << assetCount << "): "; + for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) + { + std::cout << assetNum + 1 << ".. "; + CubeAssetGenerator::Settings settings; + settings.extents = GeneratorAsset::Vec3(1, 1, 1); + settings.bondFlags = bondFlags; + CubeAssetGenerator::DepthInfo depthInfo; + depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); + depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; + settings.depths.push_back(depthInfo); + uint32_t chunkCount = 1; + while (chunkCount < minChunkCount) + { + uint32_t chunkMul; + do + { + depthInfo.slicesPerAxis = simple ? GeneratorAsset::Vec3(2, 2, 2) : GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); + chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); + } while (chunkMul == 1); + if (chunkCount*chunkMul > maxChunkCount) + { + break; + } + chunkCount *= chunkMul; + settings.depths.push_back(depthInfo); + settings.extents = settings.extents * depthInfo.slicesPerAxis; + } + settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support + + // Make largest direction unit size + settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); + + // Create asset + GeneratorAsset testAsset; + CubeAssetGenerator::generate(testAsset, settings); + + NvBlastAssetDesc desc; + desc.chunkDescs = testAsset.solverChunks.data(); + desc.chunkCount = (uint32_t)testAsset.solverChunks.size(); + desc.bondDescs = testAsset.solverBonds.data(); + desc.bondCount = (uint32_t)testAsset.solverBonds.size(); + NvBlastAsset* asset = buildAsset(desc); + NvBlastID assetID = NvBlastAssetGetID(asset, messageLog); + + // copy asset (for setAsset testing) + const char* data = (const char*)asset; + const uint32_t dataSize = NvBlastAssetGetSize(asset, messageLog); + char* duplicateData = (char*)alloc(dataSize); + memcpy(duplicateData, data, dataSize); + NvBlastAsset* assetDuplicate = (NvBlastAsset*)duplicateData; + + // Generate families + for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) + { + // family + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); // Using zeroingAlloc in case actorTest compares memory blocks + NvBlastID id = NvBlastFamilyGetAssetID(family, messageLog); + EXPECT_TRUE(!memcmp(&assetID, &id, sizeof(NvBlastID))); + if (rand() % 2 == 0) + { + // replace asset with duplicate in half of cases to test setAsset + NvBlastFamilySetAsset(family, assetDuplicate, messageLog); + NvBlastID id2 = NvBlastFamilyGetAssetID(family, messageLog); + EXPECT_TRUE(!memcmp(&assetID, &id2, sizeof(NvBlastID))); + } + + // actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + m_scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, m_scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // Generate damage + std::set actors; + actors.insert(actor); + if (printActorCount) std::cout << "Actors: 1.. "; + for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) + { + GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); + blast(actors, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage); + if (printActorCount) std::cout << actors.size() << ".. "; + if (actors.size() > 0) + { + const NvBlastFamily* family = NvBlastActorGetFamily(*actors.begin(), messageLog); + const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog); + EXPECT_EQ((uint32_t)actors.size(), actorCount); + if ((uint32_t)actors.size() == actorCount) + { + std::vector buffer1(actorCount); + const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog); + EXPECT_EQ(actorsWritten, actorCount); + std::vector buffer2(actors.begin(), actors.end()); + EXPECT_EQ(0, memcmp(&buffer1[0], buffer2.data(), actorCount*sizeof(NvBlastActor*))); + } + } + // Test individual actors + if (actorTest != nullptr) + { + for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) + { + actorTest(*static_cast(*k), messageLog); + } + } + } + if (printActorCount) std::cout << "\n"; + + // Test fractured actor set + if (postDamageTest) + { + std::vector actorArray(actors.begin(), actors.end()); + postDamageTest(actorArray, messageLog); + } + + // Release remaining actors + for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) + { + NvBlastActorDeactivate(*k, messageLog); + } + actors.clear(); + + free(family); + } + + // Release asset data + free(asset); + free(assetDuplicate); + } + std::cout << "done.\n"; + } + + std::vector m_assets; + std::vector m_actors; + std::vector m_scratch; + static std::vector s_storage; + + static size_t s_curr; +}; + +// Static values +template +std::vector ActorTest::s_storage; + +template +size_t ActorTest::s_curr; + +// Specializations +typedef ActorTest ActorTestAllowWarnings; +typedef ActorTest ActorTestStrict; + +// Tests +TEST_F(ActorTestStrict, InstanceActors) +{ + // Build assets and instance actors + buildAssets(); + instanceActors(); + + // Release actors and destroy assets + releaseActors(); + destroyAssets(); +} + +TEST_F(ActorTestAllowWarnings, ActorHealthInitialization) +{ + // Test all assets + std::vector assetDescs; + assetDescs.insert(assetDescs.end(), g_assetDescs, g_assetDescs + getAssetDescCount()); + assetDescs.insert(assetDescs.end(), g_assetDescsMissingCoverage, g_assetDescsMissingCoverage + getAssetDescMissingCoverageCount()); + + struct TestMode + { + enum Enum + { + Uniform, + Nonuniform, + + Count + }; + }; + + for (auto assetDesc : assetDescs) + { + NvBlastAsset* asset = buildAsset(assetDesc); + EXPECT_TRUE(asset != nullptr); + + Nv::Blast::Asset& assetInt = static_cast(*asset); + + NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr); + + std::vector supportChunkHealths(graph.nodeCount); + for (size_t i = 0; i < supportChunkHealths.size(); ++i) + { + supportChunkHealths[i] = 1.0f + (float)i; + } + + std::vector bondHealths(assetInt.getBondCount()); + for (size_t i = 0; i < bondHealths.size(); ++i) + { + bondHealths[i] = 1.5f + (float)i; + } + + for (int chunkTestMode = 0; chunkTestMode < TestMode::Count; ++chunkTestMode) + { + for (int bondTestMode = 0; bondTestMode < TestMode::Count; ++bondTestMode) + { + NvBlastActorDesc actorDesc; + + switch (chunkTestMode) + { + default: + case TestMode::Uniform: + actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + break; + case TestMode::Nonuniform: + actorDesc.initialSupportChunkHealths = supportChunkHealths.data(); + break; + } + + switch (bondTestMode) + { + default: + case TestMode::Uniform: + actorDesc.initialBondHealths = nullptr; + actorDesc.uniformInitialBondHealth = 2.0f; + break; + case TestMode::Nonuniform: + actorDesc.initialBondHealths = bondHealths.data(); + break; + } + + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); + std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + Nv::Blast::Actor& actorInt = static_cast(*actor); + Nv::Blast::FamilyHeader* header = actorInt.getFamilyHeader(); + + + for (uint32_t i = 0; i < graph.nodeCount; ++i) + { + const uint32_t supportChunkIndex = graph.chunkIndices[i]; + for (Nv::Blast::Asset::DepthFirstIt it(assetInt, supportChunkIndex); (bool)it; ++it) + { + const uint32_t chunkIndex = (uint32_t)it; + const uint32_t lowerSupportIndex = assetInt.getContiguousLowerSupportIndex(chunkIndex); + NVBLAST_ASSERT(lowerSupportIndex < assetInt.getLowerSupportChunkCount()); + const float health = header->getLowerSupportChunkHealths()[lowerSupportIndex]; + switch (chunkTestMode) + { + default: + case TestMode::Uniform: + EXPECT_EQ(1.0f, health); + break; + case TestMode::Nonuniform: + EXPECT_EQ(supportChunkHealths[i], health); + break; + } + } + } + + for (uint32_t i = 0; i < assetInt.getBondCount(); ++i) + { + switch (bondTestMode) + { + default: + case TestMode::Uniform: + EXPECT_EQ(2.0f, header->getBondHealths()[i]); + break; + case TestMode::Nonuniform: + EXPECT_EQ(bondHealths[i], header->getBondHealths()[i]); + break; + } + } + + NvBlastActorDeactivate(actor, messageLog); + free(family); + } + } + + free(asset); + } +} + +TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, false); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, false); +} + +TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, true); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, true); +} + +TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); +} + +TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorsTestVisibility) +{ + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorTestBlockSerialization) +{ + s_storage.resize(0); + damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockSerialize); + s_curr = 0; + damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockDeserialize); + s_storage.resize(0); +} + +TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationNewFamily) +{ + damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationNewFamily); +} + +TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationPartialBlock) +{ + damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationPartialBlock); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationNewFamily) +{ + damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationNewFamily); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationPartialBlock) +{ + damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationPartialBlock); +} + +TEST_F(ActorTestStrict, DamageMultipleIslandLeafSupportActorsTestVisibility) +{ + typedef CubeAssetGenerator::BondFlags BF; + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Y_BONDS | BF::Z_BONDS); // Only connect y-z plane islands + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Z_BONDS); // Only connect z-direction islands + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::NO_BONDS); // All support chunks disconnected (single-chunk islands) +} + +TEST_F(ActorTestStrict, DamageBoundToWorldLeafSupportActorsTestVisibility) +{ + typedef CubeAssetGenerator::BondFlags BF; + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_MINUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Y_PLUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Z_MINUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::X_MINUS_WORLD_BONDS + | BF::Y_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS + | BF::Z_PLUS_WORLD_BONDS | BF::Z_MINUS_WORLD_BONDS); +} diff --git a/test/src/unit/AssetTests.cpp b/test/src/unit/AssetTests.cpp old mode 100644 new mode 100755 index 9bd8aa0..6511f60 --- a/test/src/unit/AssetTests.cpp +++ b/test/src/unit/AssetTests.cpp @@ -1,858 +1,858 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "NvBlastAsset.h" -#include "NvBlastMath.h" - -#include "BlastBaseTest.h" - -#include "NvBlastTkFramework.h" - -#include - - -// all supported platform now provide serialization -// keep the define for future platforms that won't -#define ENABLE_SERIALIZATION_TESTS 1 - -#pragma warning( push ) -#pragma warning( disable : 4267 ) -// NOTE: Instead of excluding serialization and the tests when on VC12, should break the tests out into a separate C++ file. - -#if ENABLE_SERIALIZATION_TESTS -#include "NvBlastExtSerialization.h" -#include "NvBlastExtLlSerialization.h" -#include "NvBlastExtSerializationInternal.h" -#endif - -#include "NvBlastExtAssetUtils.h" - -#pragma warning( pop ) - -#include -#include - -#ifdef WIN32 -#include -#endif - -template -class AssetTest : public BlastBaseTest -{ -public: - - AssetTest() - { - NvBlastTkFrameworkCreate(); - } - - ~AssetTest() - { - NvBlastTkFrameworkGet()->release(); - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - BlastBaseTest::alignedFree(mem); - } - - void testSubtreeLeafChunkCounts(const Nv::Blast::Asset& a) - { - const NvBlastChunk* chunks = a.getChunks(); - const uint32_t* subtreeLeafChunkCounts = a.getSubtreeLeafChunkCounts(); - uint32_t totalLeafChunkCount = 0; - for (uint32_t chunkIndex = 0; chunkIndex < a.m_chunkCount; ++chunkIndex) - { - const NvBlastChunk& chunk = chunks[chunkIndex]; - if (Nv::Blast::isInvalidIndex(chunk.parentChunkIndex)) - { - totalLeafChunkCount += subtreeLeafChunkCounts[chunkIndex]; - } - const bool isLeafChunk = chunk.firstChildIndex >= chunk.childIndexStop; - uint32_t subtreeLeafChunkCount = isLeafChunk ? 1 : 0; - for (uint32_t childIndex = chunk.firstChildIndex; childIndex < chunk.childIndexStop; ++childIndex) - { - subtreeLeafChunkCount += subtreeLeafChunkCounts[childIndex]; - } - EXPECT_EQ(subtreeLeafChunkCount, subtreeLeafChunkCounts[chunkIndex]); - } - EXPECT_EQ(totalLeafChunkCount, a.m_leafChunkCount); - } - - void testChunkToNodeMap(const Nv::Blast::Asset& a) - { - for (uint32_t chunkIndex = 0; chunkIndex < a.m_chunkCount; ++chunkIndex) - { - const uint32_t nodeIndex = a.getChunkToGraphNodeMap()[chunkIndex]; - if (!Nv::Blast::isInvalidIndex(nodeIndex)) - { - EXPECT_LT(nodeIndex, a.m_graph.m_nodeCount); - EXPECT_EQ(chunkIndex, a.m_graph.getChunkIndices()[nodeIndex]); - } - else - { - const uint32_t* chunkIndexStop = a.m_graph.getChunkIndices() + a.m_graph.m_nodeCount; - const uint32_t* it = std::find(a.m_graph.getChunkIndices(), chunkIndexStop, chunkIndex); - EXPECT_EQ(chunkIndexStop, it); - } - } - } - - NvBlastAsset* buildAsset(const ExpectedAssetValues& expected, const NvBlastAssetDesc* desc) - { - std::vector scratch; - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(desc, messageLog)); - void* mem = alloc(NvBlastGetAssetMemorySize(desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, desc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - if (asset == nullptr) - { - free(mem); - return nullptr; - } - Nv::Blast::Asset& a = *(Nv::Blast::Asset*)asset; - EXPECT_EQ(expected.totalChunkCount, a.m_chunkCount); - EXPECT_EQ(expected.graphNodeCount, a.m_graph.m_nodeCount); - EXPECT_EQ(expected.bondCount, a.m_graph.getAdjacencyPartition()[a.m_graph.m_nodeCount] / 2); - EXPECT_EQ(expected.leafChunkCount, a.m_leafChunkCount); - EXPECT_EQ(expected.subsupportChunkCount, a.m_chunkCount - a.m_firstSubsupportChunkIndex); - testSubtreeLeafChunkCounts(a); - testChunkToNodeMap(a); - return asset; - } - - void checkAssetsExpected(Nv::Blast::Asset& asset, const ExpectedAssetValues& expected) - { - EXPECT_EQ(expected.totalChunkCount, asset.m_chunkCount); - EXPECT_EQ(expected.graphNodeCount, asset.m_graph.m_nodeCount); - EXPECT_EQ(expected.bondCount, asset.m_graph.getAdjacencyPartition()[asset.m_graph.m_nodeCount] / 2); - EXPECT_EQ(expected.leafChunkCount, asset.m_leafChunkCount); - EXPECT_EQ(expected.subsupportChunkCount, asset.m_chunkCount - asset.m_firstSubsupportChunkIndex); - testSubtreeLeafChunkCounts(asset); - testChunkToNodeMap(asset); - } - - // expects that the bond normal points from the lower indexed chunk to higher index chunk - // uses chunk.centroid - // convention, requirement from findClosestNode - void checkNormalDir(NvBlastChunkDesc* chunkDescs, size_t chunkDescCount, NvBlastBondDesc* bondDescs, size_t bondDescCount) - { - for (size_t bondIndex = 0; bondIndex < bondDescCount; ++bondIndex) - { - NvBlastBondDesc& bond = bondDescs[bondIndex]; - uint32_t chunkIndex0 = bond.chunkIndices[0]; - uint32_t chunkIndex1 = bond.chunkIndices[1]; - - bool swap = chunkIndex0 > chunkIndex1; - uint32_t testIndex0 = swap ? chunkIndex1 : chunkIndex0; - uint32_t testIndex1 = swap ? chunkIndex0 : chunkIndex1; - - EXPECT_TRUE(testIndex0 < testIndex1); - - // no convention for world chunks - if (!Nv::Blast::isInvalidIndex(testIndex0) && !Nv::Blast::isInvalidIndex(testIndex1)) - { - NvBlastChunkDesc& chunk0 = chunkDescs[testIndex0]; - NvBlastChunkDesc& chunk1 = chunkDescs[testIndex1]; - - float dir[3]; - Nv::Blast::VecMath::sub(chunk1.centroid, chunk0.centroid, dir); - bool meetsConvention = Nv::Blast::VecMath::dot(bond.bond.normal, dir) > 0; - EXPECT_TRUE(meetsConvention); - if (!meetsConvention) - { - printf("bond %zd chunks(%d,%d): %.2f %.2f %.2f %.2f %.2f %.2f %d\n", - bondIndex, chunkIndex0, chunkIndex1, - bond.bond.normal[0], bond.bond.normal[1], bond.bond.normal[2], - dir[0], dir[1], dir[2], - Nv::Blast::VecMath::dot(bond.bond.normal, dir) > 0); - } - } - } - } - - // expects that the bond normal points from the lower indexed node to higher index node - // uses chunk.centroid - // convention, requirement from findClosestNode - void checkNormalDir(const NvBlastSupportGraph graph, const NvBlastChunk* assetChunks, const NvBlastBond* assetBonds) - { - for (uint32_t nodeIndex = 0; nodeIndex < graph.nodeCount; nodeIndex++) - { - uint32_t adjStart = graph.adjacencyPartition[nodeIndex]; - uint32_t adjStop = graph.adjacencyPartition[nodeIndex + 1]; - for (uint32_t adj = adjStart; adj < adjStop; ++adj) - { - uint32_t adjNodeIndex = graph.adjacentNodeIndices[adj]; - - bool swap = nodeIndex > adjNodeIndex; - uint32_t testIndex0 = swap ? adjNodeIndex : nodeIndex; - uint32_t testIndex1 = swap ? nodeIndex : adjNodeIndex; - - // no convention for world chunks - if (!Nv::Blast::isInvalidIndex(graph.chunkIndices[testIndex0]) && !Nv::Blast::isInvalidIndex(graph.chunkIndices[testIndex1])) - { - const NvBlastChunk& chunk0 = assetChunks[graph.chunkIndices[testIndex0]]; - const NvBlastChunk& chunk1 = assetChunks[graph.chunkIndices[testIndex1]]; - - uint32_t bondIndex = graph.adjacentBondIndices[adj]; - const NvBlastBond& bond = assetBonds[bondIndex]; - - float dir[3]; - Nv::Blast::VecMath::sub(chunk1.centroid, chunk0.centroid, dir); - bool meetsConvention = Nv::Blast::VecMath::dot(bond.normal, dir) > 0; - EXPECT_TRUE(meetsConvention); - if (!meetsConvention) - { - printf("bond %d nodes(%d,%d): %.2f %.2f %.2f %.2f %.2f %.2f %d\n", - bondIndex, nodeIndex, adjNodeIndex, - bond.normal[0], bond.normal[1], bond.normal[2], - dir[0], dir[1], dir[2], - Nv::Blast::VecMath::dot(bond.normal, dir) > 0); - } - } - } - } - } - - void checkNormalDir(const NvBlastAsset* asset) - { - const NvBlastChunk* assetChunks = NvBlastAssetGetChunks(asset, nullptr); - const NvBlastBond* assetBonds = NvBlastAssetGetBonds(asset, nullptr); - const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr); - checkNormalDir(graph, assetChunks, assetBonds); - } - - void buildAssetShufflingDescriptors(const NvBlastAssetDesc* desc, const ExpectedAssetValues& expected, uint32_t shuffleCount, bool useTk) - { - NvBlastAssetDesc shuffledDesc = *desc; - std::vector chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount); - shuffledDesc.chunkDescs = chunkDescs.data(); - std::vector bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount); - shuffledDesc.bondDescs = bondDescs.data(); - if (!useTk) - { - std::vector scratch(desc->chunkCount); - NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), desc->chunkCount, scratch.data(), messageLog); - } - else - { - NvBlastTkFrameworkGet()->ensureAssetExactSupportCoverage(chunkDescs.data(), desc->chunkCount); - } - for (uint32_t i = 0; i < shuffleCount; ++i) - { - checkNormalDir(chunkDescs.data(), chunkDescs.size(), bondDescs.data(), bondDescs.size()); - shuffleAndFixChunkDescs(chunkDescs.data(), desc->chunkCount, bondDescs.data(), desc->bondCount, useTk); - checkNormalDir(chunkDescs.data(), chunkDescs.size(), bondDescs.data(), bondDescs.size()); - - NvBlastAsset* asset = buildAsset(expected, &shuffledDesc); - EXPECT_TRUE(asset != nullptr); - checkNormalDir(asset); - if (asset) - { - free(asset); - } - } - } - - void shuffleAndFixChunkDescs(NvBlastChunkDesc* chunkDescs, uint32_t chunkDescCount, NvBlastBondDesc* bondDescs, uint32_t bondDescCount, bool useTk) - { - // Create reorder array and fill with identity map - std::vector shuffledOrder(chunkDescCount); - for (uint32_t i = 0; i < chunkDescCount; ++i) - { - shuffledOrder[i] = i; - } - - // An array into which to copy the reordered descs - std::vector shuffledChunkDescs(chunkDescCount); - - std::vector scratch; - const uint32_t trials = 30; - uint32_t attempt = 0; - while(1) - { - // Shuffle the reorder array - std::random_shuffle(shuffledOrder.begin(), shuffledOrder.end()); - - // Save initial bonds - std::vector savedBondDescs(bondDescs, bondDescs + bondDescCount); - - // Shuffle chunks and bonds - NvBlastApplyAssetDescChunkReorderMap(shuffledChunkDescs.data(), chunkDescs, chunkDescCount, bondDescs, bondDescCount, shuffledOrder.data(), true, nullptr); - - // All the normals are pointing in the expected direction (they have been swapped) - checkNormalDir(shuffledChunkDescs.data(), chunkDescCount, bondDescs, bondDescCount); - checkNormalDir(chunkDescs, chunkDescCount, savedBondDescs.data(), bondDescCount); - - // Check the results - for (uint32_t i = 0; i < chunkDescCount; ++i) - { - EXPECT_EQ(chunkDescs[i].userData, shuffledChunkDescs[shuffledOrder[i]].userData); - EXPECT_TRUE(chunkDescs[i].parentChunkIndex > chunkDescCount || shuffledChunkDescs[shuffledOrder[i]].parentChunkIndex == shuffledOrder[chunkDescs[i].parentChunkIndex]); - } - for (uint32_t i = 0; i < bondDescCount; ++i) - { - for (uint32_t k = 0; k < 2; ++k) - { - if (!Nv::Blast::isInvalidIndex(savedBondDescs[i].chunkIndices[k])) - { - EXPECT_EQ(shuffledOrder[savedBondDescs[i].chunkIndices[k]], bondDescs[i].chunkIndices[k]); - } - } - } - - // Try creating asset, usually it should fail (otherwise make another attempt) - NvBlastAssetDesc desc = { chunkDescCount, shuffledChunkDescs.data(), bondDescCount, bondDescs }; - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, nullptr)); - void* mem = alloc(NvBlastGetAssetMemorySize(&desc, nullptr)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), nullptr); - if (asset == nullptr) - { - free(mem); - break; - } - else - { - free(asset); - memcpy(bondDescs, savedBondDescs.data(), sizeof(NvBlastBondDesc) * bondDescCount); - attempt++; - if (attempt >= trials) - { - GTEST_NONFATAL_FAILURE_("Shuffled chunk descs should fail asset creation (most of the time)."); - break; - } - } - } - - // Now we want to fix that order - if (!useTk) - { - std::vector chunkReorderMap(chunkDescCount); - std::vector scratch2(2 * chunkDescCount * sizeof(uint32_t)); - const bool isIdentity = NvBlastBuildAssetDescChunkReorderMap(chunkReorderMap.data(), shuffledChunkDescs.data(), chunkDescCount, scratch2.data(), messageLog); - EXPECT_FALSE(isIdentity); - NvBlastApplyAssetDescChunkReorderMap(chunkDescs, shuffledChunkDescs.data(), chunkDescCount, bondDescs, bondDescCount, chunkReorderMap.data(), true, messageLog); - } - else - { - memcpy(chunkDescs, shuffledChunkDescs.data(), chunkDescCount * sizeof(NvBlastChunkDesc)); - const bool isIdentity = NvBlastTkFrameworkGet()->reorderAssetDescChunks(chunkDescs, chunkDescCount, bondDescs, bondDescCount, nullptr, true); - EXPECT_FALSE(isIdentity); - } - } - - void mergeAssetTest(const NvBlastAssetDesc& desc, bool fail) - { - std::vector scratch; - - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); - void* mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - if (asset == nullptr) - { - free(mem); - return; - } - - // Merge two copies of this asset together - const NvBlastAsset* components[2] = { asset, asset }; - const NvcVec3 translations[2] = { { 0, 0, 0 },{ 2, 0, 0 } }; - - const NvBlastBond bond = { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }; - - NvBlastExtAssetUtilsBondDesc newBondDescs[4]; - for (int i = 0; i < 4; ++i) - { - newBondDescs[i].bond = bond; - newBondDescs[i].chunkIndices[0] = 2 * (i + 1); - newBondDescs[i].chunkIndices[1] = 2 * i + 1; - newBondDescs[i].componentIndices[0] = 0; - newBondDescs[i].componentIndices[1] = 1; - } - - // Create a merged descriptor - std::vector chunkIndexOffsets(2); - std::vector chunkReorderMap(2 * desc.chunkCount); - - NvBlastAssetDesc mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, chunkIndexOffsets.data(), chunkReorderMap.data(), 2 * desc.chunkCount); - EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); - EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); - for (uint32_t i = 0; i < 2 * desc.chunkCount; ++i) - { - EXPECT_LT(chunkReorderMap[i], 2 * desc.chunkCount); - } - EXPECT_EQ(0, chunkIndexOffsets[0]); - EXPECT_EQ(desc.chunkCount, chunkIndexOffsets[1]); - - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); - mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); - NvBlastAsset* mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); - EXPECT_TRUE(mergedAsset != nullptr); - if (mergedAsset == nullptr) - { - free(mem); - return; - } - - NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); - NVBLAST_FREE(mergedAsset); - - if (!fail) - { - mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, nullptr, chunkReorderMap.data(), 2 * desc.chunkCount); - EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); - EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); - for (uint32_t i = 0; i < 2 * desc.chunkCount; ++i) - { - EXPECT_LT(chunkReorderMap[i], 2 * desc.chunkCount); - } - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); - mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); - mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); - EXPECT_TRUE(mergedAsset != nullptr); - free(mem); - NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); - } - else - { - // We don't pass in a valid chunkReorderMap so asset creation should fail - mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, chunkIndexOffsets.data(), nullptr, 0); - EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); - EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); - EXPECT_EQ(0, chunkIndexOffsets[0]); - EXPECT_EQ(desc.chunkCount, chunkIndexOffsets[1]); - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); - mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); - mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); - EXPECT_TRUE(mergedAsset == nullptr); - free(mem); - NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); - - mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, nullptr, nullptr, 0); - EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); - EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); - mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); - mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); - EXPECT_TRUE(mergedAsset == nullptr); - free(mem); - NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); - - // We lie and say the chunkReorderMap is not large enough. It should be filled with 0xFFFFFFFF up to the size we gave - mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, nullptr, chunkReorderMap.data(), desc.chunkCount); - EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); - EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); - for (uint32_t i = 0; i < desc.chunkCount; ++i) - { - EXPECT_TRUE(Nv::Blast::isInvalidIndex(chunkReorderMap[i])); - } - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); - mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); - mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); - EXPECT_TRUE(mergedAsset == nullptr); - free(mem); - NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); - - mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, chunkIndexOffsets.data(), chunkReorderMap.data(), desc.chunkCount); - EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); - EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); - for (uint32_t i = 0; i < desc.chunkCount; ++i) - { - EXPECT_TRUE(Nv::Blast::isInvalidIndex(chunkReorderMap[i])); - } - EXPECT_EQ(0, chunkIndexOffsets[0]); - EXPECT_EQ(desc.chunkCount, chunkIndexOffsets[1]); - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); - mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); - mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); - EXPECT_TRUE(mergedAsset == nullptr); - free(mem); - NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); - } - - // Finally free the original asset - NVBLAST_FREE(asset); - } -}; - -typedef AssetTest<-1, 0> AssetTestAllowErrorsSilently; -typedef AssetTest AssetTestAllowWarningsSilently; -typedef AssetTest AssetTestAllowWarnings; -typedef AssetTest AssetTestStrict; - - -TEST_F(AssetTestStrict, BuildAssets) -{ - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - assets[i] = buildAsset(g_assetExpectedValues[i], &g_assetDescs[i]); - } - - // Destroy - for (uint32_t i = 0; i < assetDescCount; ++i) - { - if (assets[i]) - { - free(assets[i]); - } - } -} - -#if ENABLE_SERIALIZATION_TESTS -TEST_F(AssetTestStrict, SerializeAssets) -{ - Nv::Blast::ExtSerialization* ser = NvBlastExtSerializationCreate(); - EXPECT_TRUE(ser != nullptr); - - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - assets[i] = reinterpret_cast(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i])); - } - - // Serialize them - for (Nv::Blast::Asset* asset : assets) - { - void* buffer; - const uint64_t size = NvBlastExtSerializationSerializeAssetIntoBuffer(buffer, *ser, asset); - EXPECT_TRUE(size != 0); - - uint32_t objectTypeID; - uint32_t encodingID; - uint64_t dataSize = 0; - EXPECT_TRUE(ser->peekHeader(&objectTypeID, &encodingID, &dataSize, buffer, size)); - EXPECT_EQ(objectTypeID, Nv::Blast::LlObjectTypeID::Asset); - EXPECT_EQ(encodingID, ser->getSerializationEncoding()); - EXPECT_EQ(dataSize + Nv::Blast::ExtSerializationInternal::HeaderSize, size); - } - - // Destroy - for (uint32_t i = 0; i < assetDescCount; ++i) - { - if (assets[i]) - { - free(assets[i]); - } - } - - ser->release(); -} - -TEST_F(AssetTestStrict, SerializeAssetsRoundTrip) -{ - Nv::Blast::ExtSerialization* ser = NvBlastExtSerializationCreate(); - EXPECT_TRUE(ser != nullptr); - - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - assets[i] = reinterpret_cast(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i])); - } - - const uint32_t encodings[] = - { - Nv::Blast::ExtSerialization::EncodingID::CapnProtoBinary, - Nv::Blast::ExtSerialization::EncodingID::RawBinary - }; - - for (auto encoding : encodings) - { - ser->setSerializationEncoding(encoding); - - // Serialize them - for (uint32_t i = 0; i < assetDescCount; ++i) - { - Nv::Blast::Asset* asset = assets[i]; - - void* buffer; - const uint64_t size = NvBlastExtSerializationSerializeAssetIntoBuffer(buffer, *ser, asset); - EXPECT_TRUE(size != 0); - - Nv::Blast::Asset* rtAsset = reinterpret_cast(ser->deserializeFromBuffer(buffer, size)); - - //TODO: Compare assets - checkAssetsExpected(*rtAsset, g_assetExpectedValues[i]); - - free(static_cast(rtAsset)); - } - } - - // Destroy - for (uint32_t i = 0; i < assetDescCount; ++i) - { - if (assets[i]) - { - free(assets[i]); - } - } - - ser->release(); -} - -TEST_F(AssetTestStrict, SerializeAssetsRoundTripWithSkipping) -{ - Nv::Blast::ExtSerialization* ser = NvBlastExtSerializationCreate(); - EXPECT_TRUE(ser != nullptr); - - std::vector stream; - - class StreamBufferProvider : public Nv::Blast::ExtSerialization::BufferProvider - { - public: - StreamBufferProvider(std::vector& stream) : m_stream(stream), m_cursor(0) {} - - virtual void* requestBuffer(size_t size) override - { - m_stream.resize(m_cursor + size); - void* data = m_stream.data() + m_cursor; - m_cursor += size; - return data; - } - - private: - std::vector& m_stream; - size_t m_cursor; - } myStreamProvider(stream); - - ser->setBufferProvider(&myStreamProvider); - - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - assets[i] = reinterpret_cast(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i])); - } - - const uint32_t encodings[] = - { - Nv::Blast::ExtSerialization::EncodingID::CapnProtoBinary, - Nv::Blast::ExtSerialization::EncodingID::RawBinary - }; - - for (auto encoding : encodings) - { - ser->setSerializationEncoding(encoding); - - // Serialize them - for (uint32_t i = 0; i < assetDescCount; ++i) - { - void* buffer; - const uint64_t size = NvBlastExtSerializationSerializeAssetIntoBuffer(buffer, *ser, assets[i]); - EXPECT_TRUE(size != 0); - } - } - - // Deserialize from stream - const void* buffer = stream.data(); - uint64_t bufferSize = stream.size(); - for (uint32_t assetCount = 0; bufferSize; ++assetCount) - { - uint32_t objectTypeID; - uint32_t encodingID; - const bool peekSuccess = ser->peekHeader(&objectTypeID, &encodingID, nullptr, buffer, bufferSize); - EXPECT_TRUE(peekSuccess); - if (!peekSuccess) - { - break; - } - - EXPECT_EQ(Nv::Blast::LlObjectTypeID::Asset, objectTypeID); - if (assetCount < assetDescCount) - { - EXPECT_EQ(Nv::Blast::ExtSerialization::EncodingID::CapnProtoBinary, encodingID); - } - else - { - EXPECT_EQ(Nv::Blast::ExtSerialization::EncodingID::RawBinary, encodingID); - } - - const bool skip = (assetCount & 1) != 0; - - if (!skip) - { - const uint32_t assetnum = assetCount % assetDescCount; - Nv::Blast::Asset* rtAsset = reinterpret_cast(ser->deserializeFromBuffer(buffer, bufferSize)); - EXPECT_TRUE(rtAsset != nullptr); - if (rtAsset == nullptr) - { - break; - } - - //TODO: Compare assets - checkAssetsExpected(*rtAsset, g_assetExpectedValues[assetnum]); - - free(static_cast(rtAsset)); - } - - buffer = ser->skipObject(bufferSize, buffer); - } - - // Destroy - for (uint32_t i = 0; i < assetDescCount; ++i) - { - if (assets[i]) - { - free(assets[i]); - } - } - - ser->release(); -} -#endif // ENABLE_SERIALIZATION_TESTS - -TEST_F(AssetTestAllowWarnings, BuildAssetsMissingCoverage) -{ - const uint32_t assetDescCount = sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); - - std::vector assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - const NvBlastAssetDesc* desc = &g_assetDescsMissingCoverage[i]; - NvBlastAssetDesc fixedDesc = *desc; - std::vector chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount); - std::vector bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount); - std::vector chunkReorderMap(desc->chunkCount); - std::vector scratch(desc->chunkCount * sizeof(NvBlastChunkDesc)); - const bool changedCoverage = !NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog); - EXPECT_TRUE(changedCoverage); - NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog); - fixedDesc.chunkDescs = chunkDescs.data(); - fixedDesc.bondDescs = bondDescs.data(); - assets[i] = buildAsset(g_assetsFromMissingCoverageExpectedValues[i], &fixedDesc); - } - - // Destroy - for (uint32_t i = 0; i < assetDescCount; ++i) - { - if (assets[i]) - { - free(assets[i]); - } - } -} - -TEST_F(AssetTestAllowWarningsSilently, BuildAssetsShufflingChunkDescriptors) -{ - for (uint32_t i = 0; i < sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); ++i) - { - buildAssetShufflingDescriptors(&g_assetDescs[i], g_assetExpectedValues[i], 10, false); - } - - for (uint32_t i = 0; i < sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); ++i) - { - buildAssetShufflingDescriptors(&g_assetDescsMissingCoverage[i], g_assetsFromMissingCoverageExpectedValues[i], 10, false); - } -} - -TEST_F(AssetTestAllowWarningsSilently, BuildAssetsShufflingChunkDescriptorsUsingTk) -{ - for (uint32_t i = 0; i < sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); ++i) - { - buildAssetShufflingDescriptors(&g_assetDescs[i], g_assetExpectedValues[i], 10, true); - } - - for (uint32_t i = 0; i < sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); ++i) - { - buildAssetShufflingDescriptors(&g_assetDescsMissingCoverage[i], g_assetsFromMissingCoverageExpectedValues[i], 10, true); - } -} - -TEST_F(AssetTestStrict, MergeAssetsUpperSupportOnly) -{ - mergeAssetTest(g_assetDescs[0], false); -} - -TEST_F(AssetTestStrict, MergeAssetsWithSubsupport) -{ - mergeAssetTest(g_assetDescs[1], false); -} - -TEST_F(AssetTestStrict, MergeAssetsWithWorldBondsUpperSupportOnly) -{ - mergeAssetTest(g_assetDescs[3], false); -} - -TEST_F(AssetTestStrict, MergeAssetsWithWorldBondsWithSubsupport) -{ - mergeAssetTest(g_assetDescs[4], false); -} - -TEST_F(AssetTestAllowErrorsSilently, MergeAssetsUpperSupportOnlyExpectFail) -{ - mergeAssetTest(g_assetDescs[0], true); -} - -TEST_F(AssetTestAllowErrorsSilently, MergeAssetsWithSubsupportExpectFail) -{ - mergeAssetTest(g_assetDescs[1], true); -} - -TEST_F(AssetTestAllowErrorsSilently, MergeAssetsWithWorldBondsUpperSupportOnlyExpectFail) -{ - mergeAssetTest(g_assetDescs[3], true); -} - -TEST_F(AssetTestAllowErrorsSilently, MergeAssetsWithWorldBondsWithSubsupportExpectFail) -{ - mergeAssetTest(g_assetDescs[4], true); -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "NvBlastAsset.h" +#include "NvBlastMath.h" + +#include "BlastBaseTest.h" + +#include "NvBlastTkFramework.h" + +#include + + +// all supported platform now provide serialization +// keep the define for future platforms that won't +#define ENABLE_SERIALIZATION_TESTS 1 + +#pragma warning( push ) +#pragma warning( disable : 4267 ) +// NOTE: Instead of excluding serialization and the tests when on VC12, should break the tests out into a separate C++ file. + +#if ENABLE_SERIALIZATION_TESTS +#include "NvBlastExtSerialization.h" +#include "NvBlastExtLlSerialization.h" +#include "NvBlastExtSerializationInternal.h" +#endif + +#include "NvBlastExtAssetUtils.h" + +#pragma warning( pop ) + +#include +#include + +#ifdef WIN32 +#include +#endif + +template +class AssetTest : public BlastBaseTest +{ +public: + + AssetTest() + { + NvBlastTkFrameworkCreate(); + } + + ~AssetTest() + { + NvBlastTkFrameworkGet()->release(); + } + + static void messageLog(int type, const char* msg, const char* file, int line) + { + BlastBaseTest::messageLog(type, msg, file, line); + } + + static void* alloc(size_t size) + { + return BlastBaseTest::alignedZeroedAlloc(size); + } + + static void free(void* mem) + { + BlastBaseTest::alignedFree(mem); + } + + void testSubtreeLeafChunkCounts(const Nv::Blast::Asset& a) + { + const NvBlastChunk* chunks = a.getChunks(); + const uint32_t* subtreeLeafChunkCounts = a.getSubtreeLeafChunkCounts(); + uint32_t totalLeafChunkCount = 0; + for (uint32_t chunkIndex = 0; chunkIndex < a.m_chunkCount; ++chunkIndex) + { + const NvBlastChunk& chunk = chunks[chunkIndex]; + if (Nv::Blast::isInvalidIndex(chunk.parentChunkIndex)) + { + totalLeafChunkCount += subtreeLeafChunkCounts[chunkIndex]; + } + const bool isLeafChunk = chunk.firstChildIndex >= chunk.childIndexStop; + uint32_t subtreeLeafChunkCount = isLeafChunk ? 1 : 0; + for (uint32_t childIndex = chunk.firstChildIndex; childIndex < chunk.childIndexStop; ++childIndex) + { + subtreeLeafChunkCount += subtreeLeafChunkCounts[childIndex]; + } + EXPECT_EQ(subtreeLeafChunkCount, subtreeLeafChunkCounts[chunkIndex]); + } + EXPECT_EQ(totalLeafChunkCount, a.m_leafChunkCount); + } + + void testChunkToNodeMap(const Nv::Blast::Asset& a) + { + for (uint32_t chunkIndex = 0; chunkIndex < a.m_chunkCount; ++chunkIndex) + { + const uint32_t nodeIndex = a.getChunkToGraphNodeMap()[chunkIndex]; + if (!Nv::Blast::isInvalidIndex(nodeIndex)) + { + EXPECT_LT(nodeIndex, a.m_graph.m_nodeCount); + EXPECT_EQ(chunkIndex, a.m_graph.getChunkIndices()[nodeIndex]); + } + else + { + const uint32_t* chunkIndexStop = a.m_graph.getChunkIndices() + a.m_graph.m_nodeCount; + const uint32_t* it = std::find(a.m_graph.getChunkIndices(), chunkIndexStop, chunkIndex); + EXPECT_EQ(chunkIndexStop, it); + } + } + } + + NvBlastAsset* buildAsset(const ExpectedAssetValues& expected, const NvBlastAssetDesc* desc) + { + std::vector scratch; + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(desc, messageLog)); + void* mem = alloc(NvBlastGetAssetMemorySize(desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, desc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + if (asset == nullptr) + { + free(mem); + return nullptr; + } + Nv::Blast::Asset& a = *(Nv::Blast::Asset*)asset; + EXPECT_EQ(expected.totalChunkCount, a.m_chunkCount); + EXPECT_EQ(expected.graphNodeCount, a.m_graph.m_nodeCount); + EXPECT_EQ(expected.bondCount, a.m_graph.getAdjacencyPartition()[a.m_graph.m_nodeCount] / 2); + EXPECT_EQ(expected.leafChunkCount, a.m_leafChunkCount); + EXPECT_EQ(expected.subsupportChunkCount, a.m_chunkCount - a.m_firstSubsupportChunkIndex); + testSubtreeLeafChunkCounts(a); + testChunkToNodeMap(a); + return asset; + } + + void checkAssetsExpected(Nv::Blast::Asset& asset, const ExpectedAssetValues& expected) + { + EXPECT_EQ(expected.totalChunkCount, asset.m_chunkCount); + EXPECT_EQ(expected.graphNodeCount, asset.m_graph.m_nodeCount); + EXPECT_EQ(expected.bondCount, asset.m_graph.getAdjacencyPartition()[asset.m_graph.m_nodeCount] / 2); + EXPECT_EQ(expected.leafChunkCount, asset.m_leafChunkCount); + EXPECT_EQ(expected.subsupportChunkCount, asset.m_chunkCount - asset.m_firstSubsupportChunkIndex); + testSubtreeLeafChunkCounts(asset); + testChunkToNodeMap(asset); + } + + // expects that the bond normal points from the lower indexed chunk to higher index chunk + // uses chunk.centroid + // convention, requirement from findClosestNode + void checkNormalDir(NvBlastChunkDesc* chunkDescs, size_t chunkDescCount, NvBlastBondDesc* bondDescs, size_t bondDescCount) + { + for (size_t bondIndex = 0; bondIndex < bondDescCount; ++bondIndex) + { + NvBlastBondDesc& bond = bondDescs[bondIndex]; + uint32_t chunkIndex0 = bond.chunkIndices[0]; + uint32_t chunkIndex1 = bond.chunkIndices[1]; + + bool swap = chunkIndex0 > chunkIndex1; + uint32_t testIndex0 = swap ? chunkIndex1 : chunkIndex0; + uint32_t testIndex1 = swap ? chunkIndex0 : chunkIndex1; + + EXPECT_TRUE(testIndex0 < testIndex1); + + // no convention for world chunks + if (!Nv::Blast::isInvalidIndex(testIndex0) && !Nv::Blast::isInvalidIndex(testIndex1)) + { + NvBlastChunkDesc& chunk0 = chunkDescs[testIndex0]; + NvBlastChunkDesc& chunk1 = chunkDescs[testIndex1]; + + float dir[3]; + Nv::Blast::VecMath::sub(chunk1.centroid, chunk0.centroid, dir); + bool meetsConvention = Nv::Blast::VecMath::dot(bond.bond.normal, dir) > 0; + EXPECT_TRUE(meetsConvention); + if (!meetsConvention) + { + printf("bond %zd chunks(%d,%d): %.2f %.2f %.2f %.2f %.2f %.2f %d\n", + bondIndex, chunkIndex0, chunkIndex1, + bond.bond.normal[0], bond.bond.normal[1], bond.bond.normal[2], + dir[0], dir[1], dir[2], + Nv::Blast::VecMath::dot(bond.bond.normal, dir) > 0); + } + } + } + } + + // expects that the bond normal points from the lower indexed node to higher index node + // uses chunk.centroid + // convention, requirement from findClosestNode + void checkNormalDir(const NvBlastSupportGraph graph, const NvBlastChunk* assetChunks, const NvBlastBond* assetBonds) + { + for (uint32_t nodeIndex = 0; nodeIndex < graph.nodeCount; nodeIndex++) + { + uint32_t adjStart = graph.adjacencyPartition[nodeIndex]; + uint32_t adjStop = graph.adjacencyPartition[nodeIndex + 1]; + for (uint32_t adj = adjStart; adj < adjStop; ++adj) + { + uint32_t adjNodeIndex = graph.adjacentNodeIndices[adj]; + + bool swap = nodeIndex > adjNodeIndex; + uint32_t testIndex0 = swap ? adjNodeIndex : nodeIndex; + uint32_t testIndex1 = swap ? nodeIndex : adjNodeIndex; + + // no convention for world chunks + if (!Nv::Blast::isInvalidIndex(graph.chunkIndices[testIndex0]) && !Nv::Blast::isInvalidIndex(graph.chunkIndices[testIndex1])) + { + const NvBlastChunk& chunk0 = assetChunks[graph.chunkIndices[testIndex0]]; + const NvBlastChunk& chunk1 = assetChunks[graph.chunkIndices[testIndex1]]; + + uint32_t bondIndex = graph.adjacentBondIndices[adj]; + const NvBlastBond& bond = assetBonds[bondIndex]; + + float dir[3]; + Nv::Blast::VecMath::sub(chunk1.centroid, chunk0.centroid, dir); + bool meetsConvention = Nv::Blast::VecMath::dot(bond.normal, dir) > 0; + EXPECT_TRUE(meetsConvention); + if (!meetsConvention) + { + printf("bond %d nodes(%d,%d): %.2f %.2f %.2f %.2f %.2f %.2f %d\n", + bondIndex, nodeIndex, adjNodeIndex, + bond.normal[0], bond.normal[1], bond.normal[2], + dir[0], dir[1], dir[2], + Nv::Blast::VecMath::dot(bond.normal, dir) > 0); + } + } + } + } + } + + void checkNormalDir(const NvBlastAsset* asset) + { + const NvBlastChunk* assetChunks = NvBlastAssetGetChunks(asset, nullptr); + const NvBlastBond* assetBonds = NvBlastAssetGetBonds(asset, nullptr); + const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr); + checkNormalDir(graph, assetChunks, assetBonds); + } + + void buildAssetShufflingDescriptors(const NvBlastAssetDesc* desc, const ExpectedAssetValues& expected, uint32_t shuffleCount, bool useTk) + { + NvBlastAssetDesc shuffledDesc = *desc; + std::vector chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount); + shuffledDesc.chunkDescs = chunkDescs.data(); + std::vector bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount); + shuffledDesc.bondDescs = bondDescs.data(); + if (!useTk) + { + std::vector scratch(desc->chunkCount); + NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), desc->chunkCount, scratch.data(), messageLog); + } + else + { + NvBlastTkFrameworkGet()->ensureAssetExactSupportCoverage(chunkDescs.data(), desc->chunkCount); + } + for (uint32_t i = 0; i < shuffleCount; ++i) + { + checkNormalDir(chunkDescs.data(), chunkDescs.size(), bondDescs.data(), bondDescs.size()); + shuffleAndFixChunkDescs(chunkDescs.data(), desc->chunkCount, bondDescs.data(), desc->bondCount, useTk); + checkNormalDir(chunkDescs.data(), chunkDescs.size(), bondDescs.data(), bondDescs.size()); + + NvBlastAsset* asset = buildAsset(expected, &shuffledDesc); + EXPECT_TRUE(asset != nullptr); + checkNormalDir(asset); + if (asset) + { + free(asset); + } + } + } + + void shuffleAndFixChunkDescs(NvBlastChunkDesc* chunkDescs, uint32_t chunkDescCount, NvBlastBondDesc* bondDescs, uint32_t bondDescCount, bool useTk) + { + // Create reorder array and fill with identity map + std::vector shuffledOrder(chunkDescCount); + for (uint32_t i = 0; i < chunkDescCount; ++i) + { + shuffledOrder[i] = i; + } + + // An array into which to copy the reordered descs + std::vector shuffledChunkDescs(chunkDescCount); + + std::vector scratch; + const uint32_t trials = 30; + uint32_t attempt = 0; + while(1) + { + // Shuffle the reorder array + std::random_shuffle(shuffledOrder.begin(), shuffledOrder.end()); + + // Save initial bonds + std::vector savedBondDescs(bondDescs, bondDescs + bondDescCount); + + // Shuffle chunks and bonds + NvBlastApplyAssetDescChunkReorderMap(shuffledChunkDescs.data(), chunkDescs, chunkDescCount, bondDescs, bondDescCount, shuffledOrder.data(), true, nullptr); + + // All the normals are pointing in the expected direction (they have been swapped) + checkNormalDir(shuffledChunkDescs.data(), chunkDescCount, bondDescs, bondDescCount); + checkNormalDir(chunkDescs, chunkDescCount, savedBondDescs.data(), bondDescCount); + + // Check the results + for (uint32_t i = 0; i < chunkDescCount; ++i) + { + EXPECT_EQ(chunkDescs[i].userData, shuffledChunkDescs[shuffledOrder[i]].userData); + EXPECT_TRUE(chunkDescs[i].parentChunkIndex > chunkDescCount || shuffledChunkDescs[shuffledOrder[i]].parentChunkIndex == shuffledOrder[chunkDescs[i].parentChunkIndex]); + } + for (uint32_t i = 0; i < bondDescCount; ++i) + { + for (uint32_t k = 0; k < 2; ++k) + { + if (!Nv::Blast::isInvalidIndex(savedBondDescs[i].chunkIndices[k])) + { + EXPECT_EQ(shuffledOrder[savedBondDescs[i].chunkIndices[k]], bondDescs[i].chunkIndices[k]); + } + } + } + + // Try creating asset, usually it should fail (otherwise make another attempt) + NvBlastAssetDesc desc = { chunkDescCount, shuffledChunkDescs.data(), bondDescCount, bondDescs }; + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, nullptr)); + void* mem = alloc(NvBlastGetAssetMemorySize(&desc, nullptr)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), nullptr); + if (asset == nullptr) + { + free(mem); + break; + } + else + { + free(asset); + memcpy(bondDescs, savedBondDescs.data(), sizeof(NvBlastBondDesc) * bondDescCount); + attempt++; + if (attempt >= trials) + { + GTEST_NONFATAL_FAILURE_("Shuffled chunk descs should fail asset creation (most of the time)."); + break; + } + } + } + + // Now we want to fix that order + if (!useTk) + { + std::vector chunkReorderMap(chunkDescCount); + std::vector scratch2(2 * chunkDescCount * sizeof(uint32_t)); + const bool isIdentity = NvBlastBuildAssetDescChunkReorderMap(chunkReorderMap.data(), shuffledChunkDescs.data(), chunkDescCount, scratch2.data(), messageLog); + EXPECT_FALSE(isIdentity); + NvBlastApplyAssetDescChunkReorderMap(chunkDescs, shuffledChunkDescs.data(), chunkDescCount, bondDescs, bondDescCount, chunkReorderMap.data(), true, messageLog); + } + else + { + memcpy(chunkDescs, shuffledChunkDescs.data(), chunkDescCount * sizeof(NvBlastChunkDesc)); + const bool isIdentity = NvBlastTkFrameworkGet()->reorderAssetDescChunks(chunkDescs, chunkDescCount, bondDescs, bondDescCount, nullptr, true); + EXPECT_FALSE(isIdentity); + } + } + + void mergeAssetTest(const NvBlastAssetDesc& desc, bool fail) + { + std::vector scratch; + + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); + void* mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + if (asset == nullptr) + { + free(mem); + return; + } + + // Merge two copies of this asset together + const NvBlastAsset* components[2] = { asset, asset }; + const NvcVec3 translations[2] = { { 0, 0, 0 },{ 2, 0, 0 } }; + + const NvBlastBond bond = { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 1.0f, 0.0f, 0.0f }, 0 }; + + NvBlastExtAssetUtilsBondDesc newBondDescs[4]; + for (int i = 0; i < 4; ++i) + { + newBondDescs[i].bond = bond; + newBondDescs[i].chunkIndices[0] = 2 * (i + 1); + newBondDescs[i].chunkIndices[1] = 2 * i + 1; + newBondDescs[i].componentIndices[0] = 0; + newBondDescs[i].componentIndices[1] = 1; + } + + // Create a merged descriptor + std::vector chunkIndexOffsets(2); + std::vector chunkReorderMap(2 * desc.chunkCount); + + NvBlastAssetDesc mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, chunkIndexOffsets.data(), chunkReorderMap.data(), 2 * desc.chunkCount); + EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); + EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); + for (uint32_t i = 0; i < 2 * desc.chunkCount; ++i) + { + EXPECT_LT(chunkReorderMap[i], 2 * desc.chunkCount); + } + EXPECT_EQ(0, chunkIndexOffsets[0]); + EXPECT_EQ(desc.chunkCount, chunkIndexOffsets[1]); + + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); + mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); + NvBlastAsset* mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); + EXPECT_TRUE(mergedAsset != nullptr); + if (mergedAsset == nullptr) + { + free(mem); + return; + } + + NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); + NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); + NVBLAST_FREE(mergedAsset); + + if (!fail) + { + mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, nullptr, chunkReorderMap.data(), 2 * desc.chunkCount); + EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); + EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); + for (uint32_t i = 0; i < 2 * desc.chunkCount; ++i) + { + EXPECT_LT(chunkReorderMap[i], 2 * desc.chunkCount); + } + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); + mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); + mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); + EXPECT_TRUE(mergedAsset != nullptr); + free(mem); + NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); + NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); + } + else + { + // We don't pass in a valid chunkReorderMap so asset creation should fail + mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, chunkIndexOffsets.data(), nullptr, 0); + EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); + EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); + EXPECT_EQ(0, chunkIndexOffsets[0]); + EXPECT_EQ(desc.chunkCount, chunkIndexOffsets[1]); + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); + mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); + mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); + EXPECT_TRUE(mergedAsset == nullptr); + free(mem); + NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); + NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); + + mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, nullptr, nullptr, 0); + EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); + EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); + mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); + mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); + EXPECT_TRUE(mergedAsset == nullptr); + free(mem); + NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); + NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); + + // We lie and say the chunkReorderMap is not large enough. It should be filled with 0xFFFFFFFF up to the size we gave + mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, nullptr, chunkReorderMap.data(), desc.chunkCount); + EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); + EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); + for (uint32_t i = 0; i < desc.chunkCount; ++i) + { + EXPECT_TRUE(Nv::Blast::isInvalidIndex(chunkReorderMap[i])); + } + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); + mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); + mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); + EXPECT_TRUE(mergedAsset == nullptr); + free(mem); + NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); + NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); + + mergedDesc = NvBlastExtAssetUtilsMergeAssets(components, nullptr, nullptr, translations, 2, newBondDescs, 4, chunkIndexOffsets.data(), chunkReorderMap.data(), desc.chunkCount); + EXPECT_EQ(2 * desc.bondCount + 4, mergedDesc.bondCount); + EXPECT_EQ(2 * desc.chunkCount, mergedDesc.chunkCount); + for (uint32_t i = 0; i < desc.chunkCount; ++i) + { + EXPECT_TRUE(Nv::Blast::isInvalidIndex(chunkReorderMap[i])); + } + EXPECT_EQ(0, chunkIndexOffsets[0]); + EXPECT_EQ(desc.chunkCount, chunkIndexOffsets[1]); + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&mergedDesc, messageLog)); + mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&mergedDesc, messageLog)); + mergedAsset = NvBlastCreateAsset(mem, &mergedDesc, scratch.data(), messageLog); + EXPECT_TRUE(mergedAsset == nullptr); + free(mem); + NVBLAST_FREE(const_cast(mergedDesc.bondDescs)); + NVBLAST_FREE(const_cast(mergedDesc.chunkDescs)); + } + + // Finally free the original asset + NVBLAST_FREE(asset); + } +}; + +typedef AssetTest<-1, 0> AssetTestAllowErrorsSilently; +typedef AssetTest AssetTestAllowWarningsSilently; +typedef AssetTest AssetTestAllowWarnings; +typedef AssetTest AssetTestStrict; + + +TEST_F(AssetTestStrict, BuildAssets) +{ + const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + + std::vector assets(assetDescCount); + + // Build + for (uint32_t i = 0; i < assetDescCount; ++i) + { + assets[i] = buildAsset(g_assetExpectedValues[i], &g_assetDescs[i]); + } + + // Destroy + for (uint32_t i = 0; i < assetDescCount; ++i) + { + if (assets[i]) + { + free(assets[i]); + } + } +} + +#if ENABLE_SERIALIZATION_TESTS +TEST_F(AssetTestStrict, SerializeAssets) +{ + Nv::Blast::ExtSerialization* ser = NvBlastExtSerializationCreate(); + EXPECT_TRUE(ser != nullptr); + + const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + + std::vector assets(assetDescCount); + + // Build + for (uint32_t i = 0; i < assetDescCount; ++i) + { + assets[i] = reinterpret_cast(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i])); + } + + // Serialize them + for (Nv::Blast::Asset* asset : assets) + { + void* buffer; + const uint64_t size = NvBlastExtSerializationSerializeAssetIntoBuffer(buffer, *ser, asset); + EXPECT_TRUE(size != 0); + + uint32_t objectTypeID; + uint32_t encodingID; + uint64_t dataSize = 0; + EXPECT_TRUE(ser->peekHeader(&objectTypeID, &encodingID, &dataSize, buffer, size)); + EXPECT_EQ(objectTypeID, Nv::Blast::LlObjectTypeID::Asset); + EXPECT_EQ(encodingID, ser->getSerializationEncoding()); + EXPECT_EQ(dataSize + Nv::Blast::ExtSerializationInternal::HeaderSize, size); + } + + // Destroy + for (uint32_t i = 0; i < assetDescCount; ++i) + { + if (assets[i]) + { + free(assets[i]); + } + } + + ser->release(); +} + +TEST_F(AssetTestStrict, SerializeAssetsRoundTrip) +{ + Nv::Blast::ExtSerialization* ser = NvBlastExtSerializationCreate(); + EXPECT_TRUE(ser != nullptr); + + const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + + std::vector assets(assetDescCount); + + // Build + for (uint32_t i = 0; i < assetDescCount; ++i) + { + assets[i] = reinterpret_cast(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i])); + } + + const uint32_t encodings[] = + { + Nv::Blast::ExtSerialization::EncodingID::CapnProtoBinary, + Nv::Blast::ExtSerialization::EncodingID::RawBinary + }; + + for (auto encoding : encodings) + { + ser->setSerializationEncoding(encoding); + + // Serialize them + for (uint32_t i = 0; i < assetDescCount; ++i) + { + Nv::Blast::Asset* asset = assets[i]; + + void* buffer; + const uint64_t size = NvBlastExtSerializationSerializeAssetIntoBuffer(buffer, *ser, asset); + EXPECT_TRUE(size != 0); + + Nv::Blast::Asset* rtAsset = reinterpret_cast(ser->deserializeFromBuffer(buffer, size)); + + //TODO: Compare assets + checkAssetsExpected(*rtAsset, g_assetExpectedValues[i]); + + free(static_cast(rtAsset)); + } + } + + // Destroy + for (uint32_t i = 0; i < assetDescCount; ++i) + { + if (assets[i]) + { + free(assets[i]); + } + } + + ser->release(); +} + +TEST_F(AssetTestStrict, SerializeAssetsRoundTripWithSkipping) +{ + Nv::Blast::ExtSerialization* ser = NvBlastExtSerializationCreate(); + EXPECT_TRUE(ser != nullptr); + + std::vector stream; + + class StreamBufferProvider : public Nv::Blast::ExtSerialization::BufferProvider + { + public: + StreamBufferProvider(std::vector& stream) : m_stream(stream), m_cursor(0) {} + + virtual void* requestBuffer(size_t size) override + { + m_stream.resize(m_cursor + size); + void* data = m_stream.data() + m_cursor; + m_cursor += size; + return data; + } + + private: + std::vector& m_stream; + size_t m_cursor; + } myStreamProvider(stream); + + ser->setBufferProvider(&myStreamProvider); + + const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + + std::vector assets(assetDescCount); + + // Build + for (uint32_t i = 0; i < assetDescCount; ++i) + { + assets[i] = reinterpret_cast(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i])); + } + + const uint32_t encodings[] = + { + Nv::Blast::ExtSerialization::EncodingID::CapnProtoBinary, + Nv::Blast::ExtSerialization::EncodingID::RawBinary + }; + + for (auto encoding : encodings) + { + ser->setSerializationEncoding(encoding); + + // Serialize them + for (uint32_t i = 0; i < assetDescCount; ++i) + { + void* buffer; + const uint64_t size = NvBlastExtSerializationSerializeAssetIntoBuffer(buffer, *ser, assets[i]); + EXPECT_TRUE(size != 0); + } + } + + // Deserialize from stream + const void* buffer = stream.data(); + uint64_t bufferSize = stream.size(); + for (uint32_t assetCount = 0; bufferSize; ++assetCount) + { + uint32_t objectTypeID; + uint32_t encodingID; + const bool peekSuccess = ser->peekHeader(&objectTypeID, &encodingID, nullptr, buffer, bufferSize); + EXPECT_TRUE(peekSuccess); + if (!peekSuccess) + { + break; + } + + EXPECT_EQ(Nv::Blast::LlObjectTypeID::Asset, objectTypeID); + if (assetCount < assetDescCount) + { + EXPECT_EQ(Nv::Blast::ExtSerialization::EncodingID::CapnProtoBinary, encodingID); + } + else + { + EXPECT_EQ(Nv::Blast::ExtSerialization::EncodingID::RawBinary, encodingID); + } + + const bool skip = (assetCount & 1) != 0; + + if (!skip) + { + const uint32_t assetnum = assetCount % assetDescCount; + Nv::Blast::Asset* rtAsset = reinterpret_cast(ser->deserializeFromBuffer(buffer, bufferSize)); + EXPECT_TRUE(rtAsset != nullptr); + if (rtAsset == nullptr) + { + break; + } + + //TODO: Compare assets + checkAssetsExpected(*rtAsset, g_assetExpectedValues[assetnum]); + + free(static_cast(rtAsset)); + } + + buffer = ser->skipObject(bufferSize, buffer); + } + + // Destroy + for (uint32_t i = 0; i < assetDescCount; ++i) + { + if (assets[i]) + { + free(assets[i]); + } + } + + ser->release(); +} +#endif // ENABLE_SERIALIZATION_TESTS + +TEST_F(AssetTestAllowWarnings, BuildAssetsMissingCoverage) +{ + const uint32_t assetDescCount = sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); + + std::vector assets(assetDescCount); + + // Build + for (uint32_t i = 0; i < assetDescCount; ++i) + { + const NvBlastAssetDesc* desc = &g_assetDescsMissingCoverage[i]; + NvBlastAssetDesc fixedDesc = *desc; + std::vector chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount); + std::vector bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount); + std::vector chunkReorderMap(desc->chunkCount); + std::vector scratch(desc->chunkCount * sizeof(NvBlastChunkDesc)); + const bool changedCoverage = !NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog); + EXPECT_TRUE(changedCoverage); + NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog); + fixedDesc.chunkDescs = chunkDescs.data(); + fixedDesc.bondDescs = bondDescs.data(); + assets[i] = buildAsset(g_assetsFromMissingCoverageExpectedValues[i], &fixedDesc); + } + + // Destroy + for (uint32_t i = 0; i < assetDescCount; ++i) + { + if (assets[i]) + { + free(assets[i]); + } + } +} + +TEST_F(AssetTestAllowWarningsSilently, BuildAssetsShufflingChunkDescriptors) +{ + for (uint32_t i = 0; i < sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); ++i) + { + buildAssetShufflingDescriptors(&g_assetDescs[i], g_assetExpectedValues[i], 10, false); + } + + for (uint32_t i = 0; i < sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); ++i) + { + buildAssetShufflingDescriptors(&g_assetDescsMissingCoverage[i], g_assetsFromMissingCoverageExpectedValues[i], 10, false); + } +} + +TEST_F(AssetTestAllowWarningsSilently, BuildAssetsShufflingChunkDescriptorsUsingTk) +{ + for (uint32_t i = 0; i < sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); ++i) + { + buildAssetShufflingDescriptors(&g_assetDescs[i], g_assetExpectedValues[i], 10, true); + } + + for (uint32_t i = 0; i < sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); ++i) + { + buildAssetShufflingDescriptors(&g_assetDescsMissingCoverage[i], g_assetsFromMissingCoverageExpectedValues[i], 10, true); + } +} + +TEST_F(AssetTestStrict, MergeAssetsUpperSupportOnly) +{ + mergeAssetTest(g_assetDescs[0], false); +} + +TEST_F(AssetTestStrict, MergeAssetsWithSubsupport) +{ + mergeAssetTest(g_assetDescs[1], false); +} + +TEST_F(AssetTestStrict, MergeAssetsWithWorldBondsUpperSupportOnly) +{ + mergeAssetTest(g_assetDescs[3], false); +} + +TEST_F(AssetTestStrict, MergeAssetsWithWorldBondsWithSubsupport) +{ + mergeAssetTest(g_assetDescs[4], false); +} + +TEST_F(AssetTestAllowErrorsSilently, MergeAssetsUpperSupportOnlyExpectFail) +{ + mergeAssetTest(g_assetDescs[0], true); +} + +TEST_F(AssetTestAllowErrorsSilently, MergeAssetsWithSubsupportExpectFail) +{ + mergeAssetTest(g_assetDescs[1], true); +} + +TEST_F(AssetTestAllowErrorsSilently, MergeAssetsWithWorldBondsUpperSupportOnlyExpectFail) +{ + mergeAssetTest(g_assetDescs[3], true); +} + +TEST_F(AssetTestAllowErrorsSilently, MergeAssetsWithWorldBondsWithSubsupportExpectFail) +{ + mergeAssetTest(g_assetDescs[4], true); +} diff --git a/test/src/unit/AuthoringCutout.cpp b/test/src/unit/AuthoringCutout.cpp deleted file mode 100644 index 3a0d7de..0000000 --- a/test/src/unit/AuthoringCutout.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBaseTest.h" - -#include -#include - -#include "NvBlastExtAuthoringCutoutImpl.h" - - - - -template -class AuthoringCutoutConvertToIncrementalTest : public BlastBaseTest -{ -public: - AuthoringCutoutConvertToIncrementalTest() - { - - } - - std::vector* generateRectangleTrace(int32_t x1, int32_t x2, int32_t y1, int32_t y2, int32_t start, bool isCCW) - { - auto Ret = new std::vector(); - auto& T = *Ret; - T.reserve((x2 - x1) * 2 + (y2 - y1 - 1) * 2); - for (int32_t x = x1; x <= x2; x++) - { - T.push_back({ x, y1 }); - } - for (int32_t y = y1 + 1; y < y2; y++) - { - T.push_back({ x2, y }); - } - for (int32_t x = x2; x >= x1; x--) - { - T.push_back({ x, y2 }); - } - for (int32_t y = y2 - 1; y > y1; y--) - { - T.push_back({ x1, y }); - } - if (isCCW) - { - start = (int32_t)T.size() - start + 1; - } - start = start % T.size(); - if (start > 0) - { - std::vector buf; buf.resize(T.size()); - std::copy(T.begin() + start, T.end(), buf.begin()); - std::copy(T.begin(), T.begin() + start, buf.begin() + std::distance(T.begin() + start, T.end())); - T = buf; - } - if (isCCW) - { - std::reverse(T.begin(), T.end()); - } - //std::copy(buf.begin(), buf.end(), T.begin()); - return Ret; - } - - void testTwoTracesWithOffset(uint32_t w, uint32_t h, bool isCCW1, bool isCCW2) - { - uint32_t mid = w / 2; - uint32_t traceLenght = w + 1 + (h + 1) * 2; - - for (uint32_t start1 = 0; start1 < traceLenght; start1++) - { - for (uint32_t start2 = 0; start2 < traceLenght; start2++) - { - traces.push_back(generateRectangleTrace(-1, mid, -1, h, start1, isCCW1)); - traces.push_back(generateRectangleTrace(mid, w, -1, h, start2, isCCW2)); - expectedTraces.push_back(new std::vector()); - *expectedTraces.front() = *traces.front(); - expectedTraces.push_back(generateRectangleTrace(-1, w, -1, h, 0, false)); - - Nv::Blast::convertTracesToIncremental(traces); - - checkTraces(); - - freeTraces(); - } - } - } - - void testThreeTracesWithOffset(uint32_t sz, bool isCCW1, bool isCCW2, bool isCCW3) - { - uint32_t mid = sz / 2; - uint32_t traceLenght = sz * 2; - uint32_t traceLenght3 = sz * 3; - - for (uint32_t start1 = 0; start1 < traceLenght; start1++) - { - for (uint32_t start2 = 0; start2 < traceLenght; start2++) - { - for (uint32_t start3 = 0; start3 < traceLenght3; start3++) - { - traces.push_back(generateRectangleTrace(0, mid, 0, mid, start1, isCCW1)); - traces.push_back(generateRectangleTrace(mid, sz, 0, mid, start2, isCCW2)); - traces.push_back(generateRectangleTrace(0, sz, mid, sz, start3, isCCW3)); - expectedTraces.resize(traces.size()); - expectedTraces.back() = generateRectangleTrace(0, sz, 0, sz, 0, false); - - Nv::Blast::convertTracesToIncremental(traces); - - checkTraces(); - - freeTraces(); - } - } - } - } - - void checkTraces() - { - ASSERT_EQ(traces.size(), expectedTraces.size()); - for (uint32_t i = 0; i < traces.size(); i++) - { - if (expectedTraces[i] != nullptr) - { - ASSERT_TRUE(traces[i] != nullptr && traces[i]->size() > 3); - ASSERT_EQ(traces[i]->size(), expectedTraces[i]->size()); - auto it = std::find(expectedTraces[i]->begin(), expectedTraces[i]->end(), (*traces[i])[0]); - EXPECT_TRUE(it != expectedTraces[i]->end()); - - bool codirectional; - if (it + 1 != expectedTraces[i]->end()) - { - codirectional = *(it + 1) == (*traces[i])[1]; - } - else - { - codirectional = expectedTraces[i]->front() == (*traces[i])[1]; - } - for (uint32_t j = 0; j < traces[i]->size(); j++) - { - EXPECT_TRUE((*traces[i])[j] == *it); - if (codirectional) - { - it++; - if (it == expectedTraces[i]->end()) - { - it = expectedTraces[i]->begin(); - } - } - else - { - if (it == expectedTraces[i]->begin()) - { - it = expectedTraces[i]->end(); - } - it--; - } - } - } - } - } - - void freeTraces() - { - for (auto t : traces) - { - delete t; - } - traces.clear(); - - for (auto t : expectedTraces) - { - delete t; - } - expectedTraces.clear(); - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - - BlastBaseTest::alignedFree(mem); - } - - std::vector< std::vector* > traces; - std::vector< std::vector* > expectedTraces; - - static std::vector s_storage; - - static size_t s_curr; -}; - -// Static values -template -std::vector AuthoringCutoutConvertToIncrementalTest::s_storage; - -template -size_t AuthoringCutoutConvertToIncrementalTest::s_curr; - -// Specializations -typedef AuthoringCutoutConvertToIncrementalTest AuthoringCutoutConvertToIncrementalTestAllowWarnings; -typedef AuthoringCutoutConvertToIncrementalTest AuthoringCutoutConvertToIncrementalTestStrict; - -// Two traces with common segment -// ----- -// |0|1| -// ----- -//--gtest_filter=AuthoringCutoutConvertToIncrementalTestStrict.TwoTraces -TEST_F(AuthoringCutoutConvertToIncrementalTestStrict, TwoTraces) -{ - testTwoTracesWithOffset(3, 2, false, false); - testTwoTracesWithOffset(7, 4, true, true); - testTwoTracesWithOffset(7, 4, false, true); - testTwoTracesWithOffset(7, 4, true, false); -} - -// Three traces -// ----- -// |0|1| -// ----- -// | 2 | -// ----- -//--gtest_filter=AuthoringCutoutConvertToIncrementalTestStrict.ThreeTraces -TEST_F(AuthoringCutoutConvertToIncrementalTestStrict, ThreeTraces) -{ - testThreeTracesWithOffset(4, false, false, false); - testThreeTracesWithOffset(4, false, false, true); - testThreeTracesWithOffset(4, false, true, false); - testThreeTracesWithOffset(4, false, true, true); - testThreeTracesWithOffset(4, true, false, false); - testThreeTracesWithOffset(4, true, false, true); - testThreeTracesWithOffset(4, true, true, false); - testThreeTracesWithOffset(4, true, true, true); -} - -// Four traces -// ----- -// |0|1| -// ----- -// |2|3| -// ----- -//--gtest_filter=AuthoringCutoutConvertToIncrementalTestStrict.FourTraces -TEST_F(AuthoringCutoutConvertToIncrementalTestStrict, FourTraces) -{ - traces.push_back(generateRectangleTrace(0, 10, 0, 10, 0, false)); - traces.push_back(generateRectangleTrace(10,20, 0, 10, 0, false)); - traces.push_back(generateRectangleTrace(0, 10,10, 20, 0, false)); - traces.push_back(generateRectangleTrace(10,20,10, 20, 0, false)); - expectedTraces.resize(traces.size()); - expectedTraces.back() = generateRectangleTrace(0, 20, 0, 20, 0, false); - - Nv::Blast::convertTracesToIncremental(traces); - - checkTraces(); - - freeTraces(); -} - -// chess order segments (below for t = 4) -// ------------- -// | 0|10| 4|14| -// ------------- -// | 8| 2|12| 6| -// ------------- -// | 1|11| 5|15| -// ------------- -// | 9| 3|13| 7| -// ------------- -//--gtest_filter=AuthoringCutoutConvertToIncrementalTestStrict.ChessTraces -TEST_F(AuthoringCutoutConvertToIncrementalTestStrict, ChessTraces) -{ - for (int32_t t = 2; t < 9; t++) - { - int32_t sz = t * 2; - for (int32_t i = 0; i < sz; i += 2) - { - int32_t s = (i % 4); - for (int32_t j = s; j < sz; j += 4) - { - traces.push_back(generateRectangleTrace(i - 1, i + 1, j - 1, j + 1, 0, false)); - } - } - for (int32_t i = 0; i < sz; i += 2) - { - int32_t s = ((i + 2) % 4); - for (int32_t j = s; j < sz; j += 4) - { - traces.push_back(generateRectangleTrace(i - 1, i + 1, j - 1, j + 1, 0, false)); - } - } - - Nv::Blast::convertTracesToIncremental(traces); - - expectedTraces.resize(traces.size()); - expectedTraces.back() = generateRectangleTrace(-1, sz - 1, -1, sz - 1, 0, false); - - checkTraces(); - - freeTraces(); - } -} - - -// if convertTracesToIncremental tries to unite 0 and 1 traces the resulting trace breaks into outer and inner traces (hole). -// Currently we don't support it. So need to shaffle traces in a way that inner traces don't appear. -// ------- -// |0|1|2| -// |7|8|3| -// |6|5|4| -// ------- -//--gtest_filter=AuthoringCutoutConvertToIncrementalTestStrict.AvoidHoles -TEST_F(AuthoringCutoutConvertToIncrementalTestStrict, DISABLED_AvoidHoles) -{ - traces.push_back(generateRectangleTrace(0, 2, 0, 2, 0, false)); - traces.push_back(generateRectangleTrace(2, 4, 0, 2, 0, false)); - traces.push_back(generateRectangleTrace(4, 6, 0, 2, 0, false)); - traces.push_back(generateRectangleTrace(4, 6, 2, 4, 0, false)); - traces.push_back(generateRectangleTrace(4, 6, 4, 6, 0, false)); - traces.push_back(generateRectangleTrace(2, 4, 4, 6, 0, false)); - traces.push_back(generateRectangleTrace(0, 2, 4, 6, 0, false)); - traces.push_back(generateRectangleTrace(0, 2, 2, 4, 0, false)); - traces.push_back(generateRectangleTrace(2, 4, 2, 4, 0, false)); - - Nv::Blast::convertTracesToIncremental(traces); - - expectedTraces.resize(traces.size()); - expectedTraces.back() = generateRectangleTrace(0, 6, 0, 6, 0, false); - - checkTraces(); - - freeTraces(); -} - -// if convertTracesToIncremental tries to unite 0 and 1 traces the resulting trace breaks into outer and inner traces (hole). -// Currently we don't support it. So need to shaffle traces in a way that inner traces don't appear. -// --------- -// | | | -// | --- | -// | 0|2|1 | -// | --- | -// | | | -// --------- -//--gtest_filter=AuthoringCutoutConvertToIncrementalTestStrict.AvoidHoles2 -TEST_F(AuthoringCutoutConvertToIncrementalTestStrict, DISABLED_AvoidHoles2) -{ - std::vector*> rightTraces, leftTraces, expectedMid; - - leftTraces.push_back(generateRectangleTrace(-1, 2, -1, 1, 0, false)); - leftTraces.push_back(generateRectangleTrace(-1, 1, 1, 3, 0, false)); - leftTraces.push_back(generateRectangleTrace(-1, 2, 3, 5, 0, false)); - Nv::Blast::convertTracesToIncremental(leftTraces); - delete leftTraces[0]; delete leftTraces[1]; - traces.push_back(leftTraces.back()); - - rightTraces.push_back(generateRectangleTrace(2, 5, -1, 1, 0, false)); - rightTraces.push_back(generateRectangleTrace(3, 5, 1, 3, 0, false)); - rightTraces.push_back(generateRectangleTrace(2, 5, 3, 5, 0, false)); - Nv::Blast::convertTracesToIncremental(rightTraces); - delete rightTraces[0]; delete rightTraces[1]; - traces.push_back(rightTraces.back()); - - traces.push_back(generateRectangleTrace(1, 3, 1, 3, 0, false)); - - expectedTraces.push_back(new std::vector()); - *expectedTraces.front() = *traces.front(); - - expectedMid.push_back(generateRectangleTrace(-1, 2, -1, 5, 0, false)); - expectedMid.push_back(generateRectangleTrace(2, 3, 1, 3, 0, false)); - delete expectedMid[0]; - expectedTraces.push_back(expectedMid.back()); - expectedMid.push_back(generateRectangleTrace(-1, 5, -1, 5, 0, false)); - - Nv::Blast::convertTracesToIncremental(traces); - - checkTraces(); - - freeTraces(); -} diff --git a/test/src/unit/CoreTests.cpp b/test/src/unit/CoreTests.cpp old mode 100644 new mode 100755 index cb0481f..14f96fc --- a/test/src/unit/CoreTests.cpp +++ b/test/src/unit/CoreTests.cpp @@ -1,433 +1,433 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include -#include "gtest/gtest.h" - -//#include "NvBlast.h" -#include "NvBlastActor.h" -#include "NvBlastIndexFns.h" - -#include "NvBlastGlobals.h" - -#include "TestAssets.h" -#include "NvBlastActor.h" - -static void messageLog(int type, const char* msg, const char* file, int line) -{ - { - switch (type) - { - case NvBlastMessage::Error: std::cout << "NvBlast Error message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Warning: std::cout << "NvBlast Warning message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Info: std::cout << "NvBlast Info message in " << file << "(" << line << "): " << msg << "\n"; break; - case NvBlastMessage::Debug: std::cout << "NvBlast Debug message in " << file << "(" << line << "): " << msg << "\n"; break; - } - } -} - -TEST(CoreTests, IndexStartLookup) -{ - uint32_t lookup[32]; - uint32_t indices[] = {1,1,2,2,4,4,4}; - - Nv::Blast::createIndexStartLookup(lookup, 0, 30, indices, 7, 4); - - EXPECT_EQ(lookup[0], 0); - EXPECT_EQ(lookup[1], 0); - EXPECT_EQ(lookup[2], 2); - EXPECT_EQ(lookup[3], 4); - EXPECT_EQ(lookup[4], 4); - EXPECT_EQ(lookup[5], 7); - EXPECT_EQ(lookup[31], 7); -} - -#include "NvBlastGeometry.h" - -int findClosestNodeByBonds(const float point[4], const NvBlastActor* actor) -{ - const Nv::Blast::Actor* a = static_cast(actor); - const NvBlastFamily* family = NvBlastActorGetFamily(actor, messageLog); - const NvBlastAsset* asset = NvBlastFamilyGetAsset(family, messageLog); - const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, messageLog); - return Nv::Blast::findClosestNode( - point, - a->getFirstGraphNodeIndex(), - a->getFamilyHeader()->getGraphNodeIndexLinks(), - graph.adjacencyPartition, - graph.adjacentNodeIndices, - graph.adjacentBondIndices, - NvBlastAssetGetBonds(asset, messageLog), - NvBlastActorGetBondHealths(actor, messageLog), - graph.chunkIndices - ); -} - -int findClosestNodeByChunks(const float point[4], const NvBlastActor* actor) -{ - const Nv::Blast::Actor* a = static_cast(actor); - return Nv::Blast::findClosestNode( - point, - a->getFirstGraphNodeIndex(), - a->getFamilyHeader()->getGraphNodeIndexLinks(), - a->getAsset()->m_graph.getAdjacencyPartition(), - a->getAsset()->m_graph.getAdjacentNodeIndices(), - a->getAsset()->m_graph.getAdjacentBondIndices(), - a->getAsset()->getBonds(), - a->getFamilyHeader()->getBondHealths(), - a->getAsset()->getChunks(), - a->getFamilyHeader()->getLowerSupportChunkHealths(), - a->getAsset()->m_graph.getChunkIndices() - ); -} - -TEST(CoreTests, FindChunkByPosition) -{ - std::vector scratch; - const NvBlastAssetDesc& desc = g_assetDescs[0]; // 1-cube - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, nullptr)); - void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, nullptr)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), nullptr); - ASSERT_TRUE(asset != nullptr); - - uint32_t expectedNode[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; - const float positions[] = { - -2.0f, -2.0f, -2.0f, - +2.0f, -2.0f, -2.0f, - -2.0f, +2.0f, -2.0f, - +2.0f, +2.0f, -2.0f, - -2.0f, -2.0f, +2.0f, - +2.0f, -2.0f, +2.0f, - -2.0f, +2.0f, +2.0f, - +2.0f, +2.0f, +2.0f, - }; - const float* pos = &positions[0]; - - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, nullptr)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, nullptr)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); - ASSERT_TRUE(actor != nullptr); - - for (int i = 0; i < 8; ++i, pos += 3) - { - EXPECT_EQ(expectedNode[i], findClosestNodeByBonds(pos, actor)); - EXPECT_EQ(expectedNode[i], findClosestNodeByChunks(pos, actor)); - } - - EXPECT_TRUE(NvBlastActorDeactivate(actor, nullptr)); - NVBLAST_FREE(family); - NVBLAST_FREE(asset); -} - -TEST(CoreTests, FindChunkByPositionUShape) -{ - /* - considering this graph - - 4->5->6 - ^ - | - 1->2->3 - - and trying to find chunks by some position - */ - const NvBlastChunkDesc uchunks[7] = - { - // centroid volume parent idx flags ID - { {3.0f, 2.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {1.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {3.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {5.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {1.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {3.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { {5.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 } - }; - - const NvBlastBondDesc ubonds[5] = - { -// normal area centroid userData chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 1.0f, 0.0f }, 0 }, { 2, 1 } }, // index swap should not matter - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 1.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 1.0f, 2.0f, 0.0f }, 0 }, { 1, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 3.0f, 0.0f }, 0 }, { 4, 5 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 3.0f, 0.0f }, 0 }, { 5, 6 } }, - }; - - const NvBlastAssetDesc desc = { 7, uchunks, 5, ubonds }; - std::vector scratch; - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); - void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), messageLog); - ASSERT_TRUE(asset != nullptr); - - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); - ASSERT_TRUE(actor != nullptr); - - srand(100); - for (uint32_t i = 0; i < 100000; i++) - { - float rx = 20 * (float)(rand() - 1) / RAND_MAX - 10; - float ry = 20 * (float)(rand() - 1) / RAND_MAX - 10; - float rz = 0.0f; - float rpos[] = { rx, ry, rz }; - - // open boundaries - uint32_t col = std::max(0, std::min(2, int(rx / 2))); - uint32_t row = std::max(0, std::min(1, int(ry / 2))); - uint32_t expectedNode = col + row * 3; - - //printf("iteration %i: %.1f %.1f %.1f expected: %d\n", i, rpos[0], rpos[1], rpos[2], expectedNode); - { - uint32_t returnedNode = findClosestNodeByBonds(rpos, actor); - if (expectedNode != returnedNode) - findClosestNodeByBonds(rpos, actor); - EXPECT_EQ(expectedNode, returnedNode); - } - { - uint32_t returnedNode = findClosestNodeByChunks(rpos, actor); - if (expectedNode != returnedNode) - findClosestNodeByChunks(rpos, actor); - EXPECT_EQ(expectedNode, returnedNode); - } - - } - - EXPECT_TRUE(NvBlastActorDeactivate(actor, messageLog)); - - NVBLAST_FREE(family); - NVBLAST_FREE(asset); -} - -TEST(CoreTests, FindChunkByPositionLandlocked) -{ - // 7 > 8 > 9 - // ^ ^ ^ - // 4 > 5 > 6 - // ^ ^ ^ - // 1 > 2 > 3 - - // chunk 5 (node 4) is broken out (landlocked) - // find closest chunk/node on the two new actors - - const NvBlastChunkDesc chunks[10] = - { - // centroid volume parent idx flags ID - { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {1.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { {3.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {5.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { {1.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {3.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { {5.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { {1.0f, 5.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, - { {3.0f, 5.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, - { {5.0f, 5.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 9 }, - }; - - const NvBlastBondDesc bonds[12] = - { -// normal area centroid userData chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 1.0f, 0.0f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 1.0f, 0.0f }, 0 }, { 2, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 3.0f, 0.0f }, 0 }, { 4, 5 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 3.0f, 0.0f }, 0 }, { 5, 6 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 5.0f, 0.0f }, 0 }, { 7, 8 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 5.0f, 0.0f }, 0 }, { 8, 9 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 1.0f, 2.0f, 0.0f }, 0 }, { 1, 4 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 3.0f, 2.0f, 0.0f }, 0 }, { 2, 5 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 5.0f, 2.0f, 0.0f }, 0 }, { 3, 6 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 1.0f, 4.0f, 0.0f }, 0 }, { 4, 7 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 3.0f, 4.0f, 0.0f }, 0 }, { 5, 8 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 5.0f, 4.0f, 0.0f }, 0 }, { 6, 9 } }, - }; - - const NvBlastAssetDesc desc = { 10, chunks, 12, bonds }; - std::vector scratch; - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); - void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), messageLog); - ASSERT_TRUE(asset != nullptr); - - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, nullptr)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, nullptr)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); - ASSERT_TRUE(actor != nullptr); - - float point[4] = { 3.0f, 3.0f, 0.0f }; - EXPECT_EQ(4, findClosestNodeByChunks(point, actor)); - EXPECT_EQ(4, findClosestNodeByBonds(point, actor)); - - NvBlastChunkFractureData chunkBuffer[1]; - NvBlastFractureBuffers events = { 0, 1, nullptr, chunkBuffer }; - - NvBlastChunkFractureData chunkFracture = { 0, 5, 1.0f }; - NvBlastFractureBuffers commands = { 0, 1, nullptr, &chunkFracture }; - - NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); - EXPECT_EQ(1, events.chunkFractureCount); - - NvBlastActor* newActors[5]; - NvBlastActorSplitEvent splitEvent = { nullptr, newActors }; - scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); - size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, 5, scratch.data(), messageLog, nullptr); - - ASSERT_EQ(actor, newActors[1]); - - EXPECT_NE(4, findClosestNodeByChunks(point, actor)); - EXPECT_NE(4, findClosestNodeByBonds(point, actor)); - - float point2[4] = { 80.0f, 80.0f, 80.0f }; - EXPECT_EQ(4, findClosestNodeByChunks(point2, newActors[0])); - EXPECT_EQ(4, findClosestNodeByBonds(point, newActors[0])); - - for (uint32_t i = 0; i < newActorsCount; ++i) - { - EXPECT_TRUE(NvBlastActorDeactivate(newActors[i], nullptr)); - } - - NVBLAST_FREE(family); - NVBLAST_FREE(asset); -} - -TEST(CoreTests, FindClosestByChunkAccuracy) -{ - // (0,0) +---+-------+ - // | | 1 | - // | 2 +---+---+ - // | | 5 | | - // +---+---+ 4 | - // | 3 | | - // +-------+---+ (6,6) - - // random point lookup over the actor's space - // tests would fail if findClosestNodeByChunks didn't improve accuracy with the help of bonds - - const NvBlastChunkDesc chunks[6] = - { - // centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { { 4.0f, 1.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 1.0f, 2.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { { 2.0f, 5.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 5.0f, 4.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { { 3.0f, 3.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - }; - - const NvBlastBondDesc bonds[8] = - { - // normal area centroid userData chunks - { { { -1.0f, 0.0f, 0.0f }, 1.0f,{ 2.0f, 1.0f, 0.0f }, 0 },{ 1, 2 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 5.0f, 2.0f, 0.0f }, 0 },{ 1, 4 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 3.0f, 2.0f, 0.0f }, 0 },{ 5, 1 } }, - - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 1.0f, 4.0f, 0.0f }, 0 },{ 2, 3 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 2.0f, 3.0f, 0.0f }, 0 },{ 2, 5 } }, - - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 4.0f, 5.0f, 0.0f }, 0 },{ 3, 4 } }, - { { { 0.0f, -1.0f, 0.0f }, 1.0f,{ 3.0f, 4.0f, 0.0f }, 0 },{ 3, 5 } }, - - { { { -1.0f, 0.0f, 0.0f }, 1.0f,{ 4.0f, 3.0f, 0.0f }, 0 },{ 4, 5 } }, - }; - - const NvBlastAssetDesc desc = { 6, chunks, 8, bonds }; - std::vector scratch; - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); - void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), messageLog); - ASSERT_TRUE(asset != nullptr); - - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, nullptr)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, nullptr)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); - ASSERT_TRUE(actor != nullptr); - - srand(0xb007); - for (uint32_t i = 0; i < 100000; i++) - { - float rx = 8 * (float)(rand()) / RAND_MAX - 1; - float ry = 8 * (float)(rand()) / RAND_MAX - 1; - float rz = 0.0f; - float rpos[] = { rx, ry, rz }; - - EXPECT_LE(-1.0f, rx); EXPECT_GE(7.0f, rx); - EXPECT_LE(-1.0f, ry); EXPECT_GE(7.0f, ry); - - uint32_t expectedNode = 0xdefec7; - - if (rx < 2.0f) { - if (ry < 4.0f) { expectedNode = 1; } - else { expectedNode = 2; } - } - else if (rx < 4.0f) { - if (ry < 2.0f) { expectedNode = 0; } - else if (ry < 4.0f) { expectedNode = 4; } - else { expectedNode = 2; } - } - else { - if (ry < 2.0f) { expectedNode = 0; } - else { expectedNode = 3; } - } - - uint32_t nodeByBonds = findClosestNodeByBonds(rpos, actor); - if (nodeByBonds != expectedNode) - { - printf("%.1f %.1f %.1f\n", rx, ry, rz); - } - EXPECT_EQ(expectedNode, nodeByBonds); - - uint32_t nodeByChunks = findClosestNodeByChunks(rpos, actor); - if (nodeByChunks != expectedNode) - { - printf("%.1f %.1f %.1f\n", rx, ry, rz); - } - EXPECT_EQ(expectedNode, nodeByChunks); - } - - EXPECT_TRUE(NvBlastActorDeactivate(actor, messageLog)); - - NVBLAST_FREE(family); - NVBLAST_FREE(asset); -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include +#include "gtest/gtest.h" + +//#include "NvBlast.h" +#include "NvBlastActor.h" +#include "NvBlastIndexFns.h" + +#include "NvBlastGlobals.h" + +#include "TestAssets.h" +#include "NvBlastActor.h" + +static void messageLog(int type, const char* msg, const char* file, int line) +{ + { + switch (type) + { + case NvBlastMessage::Error: std::cout << "NvBlast Error message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Warning: std::cout << "NvBlast Warning message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Info: std::cout << "NvBlast Info message in " << file << "(" << line << "): " << msg << "\n"; break; + case NvBlastMessage::Debug: std::cout << "NvBlast Debug message in " << file << "(" << line << "): " << msg << "\n"; break; + } + } +} + +TEST(CoreTests, IndexStartLookup) +{ + uint32_t lookup[32]; + uint32_t indices[] = {1,1,2,2,4,4,4}; + + Nv::Blast::createIndexStartLookup(lookup, 0, 30, indices, 7, 4); + + EXPECT_EQ(lookup[0], 0); + EXPECT_EQ(lookup[1], 0); + EXPECT_EQ(lookup[2], 2); + EXPECT_EQ(lookup[3], 4); + EXPECT_EQ(lookup[4], 4); + EXPECT_EQ(lookup[5], 7); + EXPECT_EQ(lookup[31], 7); +} + +#include "NvBlastGeometry.h" + +int findClosestNodeByBonds(const float point[4], const NvBlastActor* actor) +{ + const Nv::Blast::Actor* a = static_cast(actor); + const NvBlastFamily* family = NvBlastActorGetFamily(actor, messageLog); + const NvBlastAsset* asset = NvBlastFamilyGetAsset(family, messageLog); + const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, messageLog); + return Nv::Blast::findClosestNode( + point, + a->getFirstGraphNodeIndex(), + a->getFamilyHeader()->getGraphNodeIndexLinks(), + graph.adjacencyPartition, + graph.adjacentNodeIndices, + graph.adjacentBondIndices, + NvBlastAssetGetBonds(asset, messageLog), + NvBlastActorGetBondHealths(actor, messageLog), + graph.chunkIndices + ); +} + +int findClosestNodeByChunks(const float point[4], const NvBlastActor* actor) +{ + const Nv::Blast::Actor* a = static_cast(actor); + return Nv::Blast::findClosestNode( + point, + a->getFirstGraphNodeIndex(), + a->getFamilyHeader()->getGraphNodeIndexLinks(), + a->getAsset()->m_graph.getAdjacencyPartition(), + a->getAsset()->m_graph.getAdjacentNodeIndices(), + a->getAsset()->m_graph.getAdjacentBondIndices(), + a->getAsset()->getBonds(), + a->getFamilyHeader()->getBondHealths(), + a->getAsset()->getChunks(), + a->getFamilyHeader()->getLowerSupportChunkHealths(), + a->getAsset()->m_graph.getChunkIndices() + ); +} + +TEST(CoreTests, FindChunkByPosition) +{ + std::vector scratch; + const NvBlastAssetDesc& desc = g_assetDescs[0]; // 1-cube + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, nullptr)); + void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, nullptr)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), nullptr); + ASSERT_TRUE(asset != nullptr); + + uint32_t expectedNode[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + const float positions[] = { + -2.0f, -2.0f, -2.0f, + +2.0f, -2.0f, -2.0f, + -2.0f, +2.0f, -2.0f, + +2.0f, +2.0f, -2.0f, + -2.0f, -2.0f, +2.0f, + +2.0f, -2.0f, +2.0f, + -2.0f, +2.0f, +2.0f, + +2.0f, +2.0f, +2.0f, + }; + const float* pos = &positions[0]; + + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, nullptr)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, nullptr)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); + ASSERT_TRUE(actor != nullptr); + + for (int i = 0; i < 8; ++i, pos += 3) + { + EXPECT_EQ(expectedNode[i], findClosestNodeByBonds(pos, actor)); + EXPECT_EQ(expectedNode[i], findClosestNodeByChunks(pos, actor)); + } + + EXPECT_TRUE(NvBlastActorDeactivate(actor, nullptr)); + NVBLAST_FREE(family); + NVBLAST_FREE(asset); +} + +TEST(CoreTests, FindChunkByPositionUShape) +{ + /* + considering this graph + + 4->5->6 + ^ + | + 1->2->3 + + and trying to find chunks by some position + */ + const NvBlastChunkDesc uchunks[7] = + { + // centroid volume parent idx flags ID + { {3.0f, 2.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {1.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {3.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {5.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {1.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {3.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { {5.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 } + }; + + const NvBlastBondDesc ubonds[5] = + { +// normal area centroid userData chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 1.0f, 0.0f }, 0 }, { 2, 1 } }, // index swap should not matter + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 1.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 1.0f, 2.0f, 0.0f }, 0 }, { 1, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 3.0f, 0.0f }, 0 }, { 4, 5 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 3.0f, 0.0f }, 0 }, { 5, 6 } }, + }; + + const NvBlastAssetDesc desc = { 7, uchunks, 5, ubonds }; + std::vector scratch; + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); + void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), messageLog); + ASSERT_TRUE(asset != nullptr); + + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); + ASSERT_TRUE(actor != nullptr); + + srand(100); + for (uint32_t i = 0; i < 100000; i++) + { + float rx = 20 * (float)(rand() - 1) / RAND_MAX - 10; + float ry = 20 * (float)(rand() - 1) / RAND_MAX - 10; + float rz = 0.0f; + float rpos[] = { rx, ry, rz }; + + // open boundaries + uint32_t col = std::max(0, std::min(2, int(rx / 2))); + uint32_t row = std::max(0, std::min(1, int(ry / 2))); + uint32_t expectedNode = col + row * 3; + + //printf("iteration %i: %.1f %.1f %.1f expected: %d\n", i, rpos[0], rpos[1], rpos[2], expectedNode); + { + uint32_t returnedNode = findClosestNodeByBonds(rpos, actor); + if (expectedNode != returnedNode) + findClosestNodeByBonds(rpos, actor); + EXPECT_EQ(expectedNode, returnedNode); + } + { + uint32_t returnedNode = findClosestNodeByChunks(rpos, actor); + if (expectedNode != returnedNode) + findClosestNodeByChunks(rpos, actor); + EXPECT_EQ(expectedNode, returnedNode); + } + + } + + EXPECT_TRUE(NvBlastActorDeactivate(actor, messageLog)); + + NVBLAST_FREE(family); + NVBLAST_FREE(asset); +} + +TEST(CoreTests, FindChunkByPositionLandlocked) +{ + // 7 > 8 > 9 + // ^ ^ ^ + // 4 > 5 > 6 + // ^ ^ ^ + // 1 > 2 > 3 + + // chunk 5 (node 4) is broken out (landlocked) + // find closest chunk/node on the two new actors + + const NvBlastChunkDesc chunks[10] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {1.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {3.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {5.0f, 1.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {1.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {3.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { {5.0f, 3.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {1.0f, 5.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, + { {3.0f, 5.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, + { {5.0f, 5.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 9 }, + }; + + const NvBlastBondDesc bonds[12] = + { +// normal area centroid userData chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 1.0f, 0.0f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 1.0f, 0.0f }, 0 }, { 2, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 3.0f, 0.0f }, 0 }, { 4, 5 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 3.0f, 0.0f }, 0 }, { 5, 6 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 5.0f, 0.0f }, 0 }, { 7, 8 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 4.0f, 5.0f, 0.0f }, 0 }, { 8, 9 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 1.0f, 2.0f, 0.0f }, 0 }, { 1, 4 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 3.0f, 2.0f, 0.0f }, 0 }, { 2, 5 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 5.0f, 2.0f, 0.0f }, 0 }, { 3, 6 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 1.0f, 4.0f, 0.0f }, 0 }, { 4, 7 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 3.0f, 4.0f, 0.0f }, 0 }, { 5, 8 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 5.0f, 4.0f, 0.0f }, 0 }, { 6, 9 } }, + }; + + const NvBlastAssetDesc desc = { 10, chunks, 12, bonds }; + std::vector scratch; + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); + void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), messageLog); + ASSERT_TRUE(asset != nullptr); + + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, nullptr)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, nullptr)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); + ASSERT_TRUE(actor != nullptr); + + float point[4] = { 3.0f, 3.0f, 0.0f }; + EXPECT_EQ(4, findClosestNodeByChunks(point, actor)); + EXPECT_EQ(4, findClosestNodeByBonds(point, actor)); + + NvBlastChunkFractureData chunkBuffer[1]; + NvBlastFractureBuffers events = { 0, 1, nullptr, chunkBuffer }; + + NvBlastChunkFractureData chunkFracture = { 0, 5, 1.0f }; + NvBlastFractureBuffers commands = { 0, 1, nullptr, &chunkFracture }; + + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_EQ(1, events.chunkFractureCount); + + NvBlastActor* newActors[5]; + NvBlastActorSplitEvent splitEvent = { nullptr, newActors }; + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, 5, scratch.data(), messageLog, nullptr); + + ASSERT_EQ(actor, newActors[1]); + + EXPECT_NE(4, findClosestNodeByChunks(point, actor)); + EXPECT_NE(4, findClosestNodeByBonds(point, actor)); + + float point2[4] = { 80.0f, 80.0f, 80.0f }; + EXPECT_EQ(4, findClosestNodeByChunks(point2, newActors[0])); + EXPECT_EQ(4, findClosestNodeByBonds(point, newActors[0])); + + for (uint32_t i = 0; i < newActorsCount; ++i) + { + EXPECT_TRUE(NvBlastActorDeactivate(newActors[i], nullptr)); + } + + NVBLAST_FREE(family); + NVBLAST_FREE(asset); +} + +TEST(CoreTests, FindClosestByChunkAccuracy) +{ + // (0,0) +---+-------+ + // | | 1 | + // | 2 +---+---+ + // | | 5 | | + // +---+---+ 4 | + // | 3 | | + // +-------+---+ (6,6) + + // random point lookup over the actor's space + // tests would fail if findClosestNodeByChunks didn't improve accuracy with the help of bonds + + const NvBlastChunkDesc chunks[6] = + { + // centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { { 4.0f, 1.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 1.0f, 2.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { { 2.0f, 5.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 5.0f, 4.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { { 3.0f, 3.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + }; + + const NvBlastBondDesc bonds[8] = + { + // normal area centroid userData chunks + { { { -1.0f, 0.0f, 0.0f }, 1.0f,{ 2.0f, 1.0f, 0.0f }, 0 },{ 1, 2 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 5.0f, 2.0f, 0.0f }, 0 },{ 1, 4 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 3.0f, 2.0f, 0.0f }, 0 },{ 5, 1 } }, + + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 1.0f, 4.0f, 0.0f }, 0 },{ 2, 3 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 2.0f, 3.0f, 0.0f }, 0 },{ 2, 5 } }, + + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 4.0f, 5.0f, 0.0f }, 0 },{ 3, 4 } }, + { { { 0.0f, -1.0f, 0.0f }, 1.0f,{ 3.0f, 4.0f, 0.0f }, 0 },{ 3, 5 } }, + + { { { -1.0f, 0.0f, 0.0f }, 1.0f,{ 4.0f, 3.0f, 0.0f }, 0 },{ 4, 5 } }, + }; + + const NvBlastAssetDesc desc = { 6, chunks, 8, bonds }; + std::vector scratch; + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); + void* amem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &desc, scratch.data(), messageLog); + ASSERT_TRUE(asset != nullptr); + + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = NVBLAST_ALLOC(NvBlastAssetGetFamilyMemorySize(asset, nullptr)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, nullptr)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), nullptr); + ASSERT_TRUE(actor != nullptr); + + srand(0xb007); + for (uint32_t i = 0; i < 100000; i++) + { + float rx = 8 * (float)(rand()) / RAND_MAX - 1; + float ry = 8 * (float)(rand()) / RAND_MAX - 1; + float rz = 0.0f; + float rpos[] = { rx, ry, rz }; + + EXPECT_LE(-1.0f, rx); EXPECT_GE(7.0f, rx); + EXPECT_LE(-1.0f, ry); EXPECT_GE(7.0f, ry); + + uint32_t expectedNode = 0xdefec7; + + if (rx < 2.0f) { + if (ry < 4.0f) { expectedNode = 1; } + else { expectedNode = 2; } + } + else if (rx < 4.0f) { + if (ry < 2.0f) { expectedNode = 0; } + else if (ry < 4.0f) { expectedNode = 4; } + else { expectedNode = 2; } + } + else { + if (ry < 2.0f) { expectedNode = 0; } + else { expectedNode = 3; } + } + + uint32_t nodeByBonds = findClosestNodeByBonds(rpos, actor); + if (nodeByBonds != expectedNode) + { + printf("%.1f %.1f %.1f\n", rx, ry, rz); + } + EXPECT_EQ(expectedNode, nodeByBonds); + + uint32_t nodeByChunks = findClosestNodeByChunks(rpos, actor); + if (nodeByChunks != expectedNode) + { + printf("%.1f %.1f %.1f\n", rx, ry, rz); + } + EXPECT_EQ(expectedNode, nodeByChunks); + } + + EXPECT_TRUE(NvBlastActorDeactivate(actor, messageLog)); + + NVBLAST_FREE(family); + NVBLAST_FREE(asset); +} diff --git a/test/src/unit/FamilyGraphTests.cpp b/test/src/unit/FamilyGraphTests.cpp old mode 100644 new mode 100755 index d0a872d..bfbea01 --- a/test/src/unit/FamilyGraphTests.cpp +++ b/test/src/unit/FamilyGraphTests.cpp @@ -1,405 +1,405 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBaseTest.h" - -#include "NvBlastSupportGraph.h" -#include "NvBlastFamilyGraph.h" -#include "NvBlastAssert.h" -#include "NvBlastIndexFns.h" - -#include -#include -#include -#include -#include - - -// ==================================================================================================================== -// HELPERS -// ==================================================================================================================== - -::testing::AssertionResult VectorMatch(const std::vector& actual, const uint32_t* expected, uint32_t size) -{ - for (size_t i(0); i < size; ++i) - { - if (expected[i] != actual[i]) - { - testing::Message msg; - msg << "array[" << i - << "] (" << actual[i] << ") != expected[" << i - << "] (" << expected[i] << ")"; - return (::testing::AssertionFailure(msg));; - } - } - - return ::testing::AssertionSuccess(); -} - -#define VECTOR_MATCH(actual, ...) \ -{ \ - const uint32_t arr[] = { __VA_ARGS__ }; \ - const uint32_t size = (sizeof(arr) / sizeof(arr[0])); \ - EXPECT_EQ(size, actual.size()); \ - EXPECT_TRUE(VectorMatch(actual, arr, size)); \ -} - - -// ==================================================================================================================== -// TEST CLASS -// ==================================================================================================================== - -using namespace Nv::Blast; - -template -class FamilyGraphTest : public BlastBaseTest < FailLevel, Verbosity > -{ -public: - FamilyGraphTest() - { - } - -protected: - FamilyGraph* buildFamilyGraph(uint32_t chunkCount, const uint32_t* adjacentChunkPartition, const uint32_t* adjacentChunkIndices) - { - NVBLAST_ASSERT(m_memoryBlock.size() == 0); // can't build twice per test - - // Fill SupportGraph with data: - NvBlastCreateOffsetStart(sizeof(SupportGraph)); - const size_t NvBlastCreateOffsetAlign16(chunkIndicesOffset, chunkCount*sizeof(uint32_t)); - const size_t NvBlastCreateOffsetAlign16(adjacencyPartitionOffset, (chunkCount + 1)*sizeof(uint32_t)); - const size_t NvBlastCreateOffsetAlign16(adjacentNodeIndicesOffset, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); - const size_t NvBlastCreateOffsetAlign16(adjacentBondIndicesOffset, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); - const size_t graphDataSize = NvBlastCreateOffsetEndAlign16(); - m_graphMemory.resize(graphDataSize); - m_graph = reinterpret_cast(m_graphMemory.data()); - - m_graph->m_nodeCount = chunkCount; - m_graph->m_chunkIndicesOffset = static_cast(chunkIndicesOffset); - m_graph->m_adjacencyPartitionOffset = static_cast(adjacencyPartitionOffset); - m_graph->m_adjacentNodeIndicesOffset = static_cast(adjacentNodeIndicesOffset); - m_graph->m_adjacentBondIndicesOffset = static_cast(adjacentBondIndicesOffset); - - memcpy(m_graph->getAdjacencyPartition(), adjacentChunkPartition, (chunkCount + 1) * sizeof(uint32_t)); - memcpy(m_graph->getAdjacentNodeIndices(), adjacentChunkIndices, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); - - // fill bondIndices by incrementing bondIndex and putting same bondIndex in mirror bond index for (n0, n1) == (n1, n0) - memset(m_graph->getAdjacentBondIndices(), (uint32_t)-1, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); - uint32_t bondIndex = 0; - for (uint32_t chunk0 = 0; chunk0 < m_graph->m_nodeCount; chunk0++) - { - for (uint32_t i = m_graph->getAdjacencyPartition()[chunk0]; i < m_graph->getAdjacencyPartition()[chunk0 + 1]; i++) - { - if (m_graph->getAdjacentBondIndices()[i] == (uint32_t)-1) - { - m_graph->getAdjacentBondIndices()[i] = bondIndex; - - uint32_t chunk1 = m_graph->getAdjacentNodeIndices()[i]; - for (uint32_t j = m_graph->getAdjacencyPartition()[chunk1]; j < m_graph->getAdjacencyPartition()[chunk1 + 1]; j++) - { - if (m_graph->getAdjacentNodeIndices()[j] == chunk0) - { - m_graph->getAdjacentBondIndices()[j] = bondIndex; - } - } - bondIndex++; - } - } - } - - // reserve memory for family graph and asset pointer - uint32_t familyGraphMemorySize = (uint32_t)FamilyGraph::requiredMemorySize(m_graph->m_nodeCount, bondIndex); - m_memoryBlock.resize(familyGraphMemorySize); - // placement new family graph - FamilyGraph* familyGraph = new(m_memoryBlock.data()) FamilyGraph(m_graph); - - return familyGraph; - } - - struct IslandInfo - { - std::vector nodes; - }; - - /** - Function to gather islands info for tests and debug purposes - Returned islands sorted by nodes counts. Island nodes also sorted by NodeIndex. - */ - void getIslandsInfo(const FamilyGraph& graph, std::vector& info) - { - IslandId* islandIds = graph.getIslandIds(); - - std::map islandMap; - - for (NodeIndex n = 0; n < m_graph->m_nodeCount; n++) - { - EXPECT_TRUE(islandIds[n] != invalidIndex()); - IslandId islandId = islandIds[n]; - if (islandMap.find(islandId) == islandMap.end()) - { - IslandInfo info; - info.nodes.push_back(n); - islandMap[islandId] = info; - } - else - { - islandMap[islandId].nodes.push_back(n); - } - } - - for (auto it = islandMap.begin(); it != islandMap.end(); ++it) - { - std::sort(it->second.nodes.begin(), it->second.nodes.end()); - info.push_back(it->second); - } - - // sort islands by size ascending - std::sort(info.begin(), info.end(), [](const IslandInfo& i0, const IslandInfo& i1) -> bool - { - size_t s0 = i0.nodes.size(); - size_t s1 = i1.nodes.size(); - if (s0 == s1 && s0 > 0) - { - s0 = i0.nodes[0]; - s1 = i1.nodes[0]; - } - return s0 < s1; - }); - } - - static const uint32_t DEFAULT_ACTOR_INDEX = 0; - - SupportGraph* m_graph; - std::vector m_graphMemory; - std::vector m_memoryBlock; -}; - -typedef FamilyGraphTest FamilyGraphTestAllowWarnings; -typedef FamilyGraphTest FamilyGraphTestStrict; - - -// ==================================================================================================================== -// GRAPH DATA -// ==================================================================================================================== - -// Graph 0: -// -// 0 -- 1 -- 2 -- 3 -// | | | | -// | | | | -// 4 -- 5 6 -- 7 -// -const uint32_t chunkCount0 = 8; -const uint32_t adjacentChunkPartition0[] = { 0, 2, 5, 8, 10, 12, 14, 16, 18 }; -const uint32_t adjacentChunkIndices0[] = { /*0*/ 1, 4, /*1*/ 0, 2, 5, /*2*/ 1, 3, 6, /*3*/ 2, 7, /*4*/ 0, 5, /*5*/ 1, 4, /*6*/ 2, 7, /*7*/ 3, 6 }; - - -// Graph 1: -// -// 0 -- 1 -- 2 -- 3 -// | | | | -// 4 -- 5 -- 6 -- 7 -// | | | | -// 8 -- 9 -- 10-- 11 -// -const uint32_t chunkCount1 = 12; -const uint32_t adjacentChunkPartition1[] = { 0, 2, 5, 8, 10, 13, 17, 21, 24, 26, 29, 32, 34 }; -const uint32_t adjacentChunkIndices1[] = { /*0*/ 1, 4, /*1*/ 0, 2, 5, /*2*/ 1, 3, 6, /*3*/ 2, 7, /*4*/ 0, 5, 8, /*5*/ 1, 4, 6, 9, /*6*/ 2, 5, 7, 10, - /*7*/ 3, 6, 11, /*8*/ 4, 9, /*9*/ 5, 8, 10, /*10*/ 6, 9, 11, /*11*/ 7, 10 }; - - -// ==================================================================================================================== -// TESTS -// ==================================================================================================================== - -TEST_F(FamilyGraphTestStrict, Graph0FindIslands0) -{ - FamilyGraph* graph = buildFamilyGraph(chunkCount0, adjacentChunkPartition0, adjacentChunkIndices0); - graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); - - std::vector scratch; - scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount0)); - - EXPECT_EQ(9, graph->getEdgesCount(m_graph)); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); - EXPECT_EQ(8, graph->getEdgesCount(m_graph)); - EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 2, m_graph); - EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - - std::vector info; - getIslandsInfo(*graph, info); - EXPECT_EQ(2, info.size()); - VECTOR_MATCH(info[0].nodes, 0, 1, 4, 5); - VECTOR_MATCH(info[1].nodes, 2, 3, 6, 7); -} - -TEST_F(FamilyGraphTestStrict, Graph0FindIslands1) -{ - FamilyGraph* graph = buildFamilyGraph(chunkCount0, adjacentChunkPartition0, adjacentChunkIndices0); - graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); - - std::vector scratch; - scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount0)); - - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 4, 5, m_graph); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 2, m_graph); - EXPECT_EQ(6, graph->getEdgesCount(m_graph)); - EXPECT_EQ(3, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - - std::vector info; - getIslandsInfo(*graph, info); - EXPECT_EQ(3, info.size()); - VECTOR_MATCH(info[0].nodes, 4); - VECTOR_MATCH(info[1].nodes, 0, 1, 5); - VECTOR_MATCH(info[2].nodes, 2, 3, 6, 7); -} - -TEST_F(FamilyGraphTestStrict, Graph0FindIslandsDifferentActors) -{ - const uint32_t ACTOR_0_INDEX = 5; - const uint32_t ACTOR_1_INDEX = 2; - - FamilyGraph* graph = buildFamilyGraph(chunkCount0, adjacentChunkPartition0, adjacentChunkIndices0); - graph->initialize(ACTOR_0_INDEX, m_graph); - - std::vector scratch; - scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount0)); - - EXPECT_EQ(0, graph->findIslands(ACTOR_1_INDEX, scratch.data(), m_graph)); - EXPECT_EQ(1, graph->findIslands(ACTOR_0_INDEX, scratch.data(), m_graph)); - - graph->notifyEdgeRemoved(ACTOR_0_INDEX, 2, 1, m_graph); - EXPECT_EQ(8, graph->getEdgesCount(m_graph)); - - EXPECT_EQ(1, graph->findIslands(ACTOR_0_INDEX, scratch.data(), m_graph)); - - graph->notifyEdgeRemoved(ACTOR_1_INDEX, 2, 6, m_graph); - graph->notifyEdgeRemoved(ACTOR_1_INDEX, 7, 3, m_graph); - EXPECT_EQ(1, graph->findIslands(ACTOR_1_INDEX, scratch.data(), m_graph)); - - graph->notifyEdgeRemoved(ACTOR_0_INDEX, 0, 1, m_graph); - graph->notifyEdgeRemoved(ACTOR_0_INDEX, 4, 5, m_graph); - EXPECT_EQ(1, graph->findIslands(ACTOR_0_INDEX, scratch.data(), m_graph)); - - - std::vector info; - getIslandsInfo(*graph, info); - EXPECT_EQ(4, info.size()); - VECTOR_MATCH(info[0].nodes, 0, 4); - VECTOR_MATCH(info[1].nodes, 1, 5); - VECTOR_MATCH(info[2].nodes, 2, 3); - VECTOR_MATCH(info[3].nodes, 6, 7); -} - -TEST_F(FamilyGraphTestStrict, Graph1FindIslands0) -{ - FamilyGraph* graph = buildFamilyGraph(chunkCount1, adjacentChunkPartition1, adjacentChunkIndices1); - graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); - - std::vector scratch; - scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount1)); - - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 5, m_graph); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 2, 6, m_graph); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 3, 7, m_graph); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 5, 6, m_graph); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 9, 10, m_graph); - EXPECT_EQ(11, graph->getEdgesCount(m_graph)); - EXPECT_EQ(3, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - - std::vector info; - getIslandsInfo(*graph, info); - EXPECT_EQ(3, info.size()); - - VECTOR_MATCH(info[0].nodes, 0, 1, 2, 3); - VECTOR_MATCH(info[1].nodes, 4, 5, 8, 9); - VECTOR_MATCH(info[2].nodes, 6, 7, 10, 11); -} - -TEST_F(FamilyGraphTestStrict, Graph1FindIslands1) -{ - FamilyGraph* graph = buildFamilyGraph(chunkCount1, adjacentChunkPartition1, adjacentChunkIndices1); - graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); - - std::vector scratch; - scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount1)); - - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); - EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 5, m_graph); - EXPECT_EQ(0, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 2, 6, m_graph); - EXPECT_EQ(0, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 3, 7, m_graph); - EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 5, 6, m_graph); - EXPECT_EQ(0, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 9, 10, m_graph); - EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - - std::vector info; - getIslandsInfo(*graph, info); - EXPECT_EQ(3, info.size()); - VECTOR_MATCH(info[0].nodes, 0, 1, 2, 3); - VECTOR_MATCH(info[1].nodes, 4, 5, 8, 9); - VECTOR_MATCH(info[2].nodes, 6, 7, 10, 11); -} - -TEST_F(FamilyGraphTestStrict, Graph1FindIslandsRemoveAllEdges) -{ - FamilyGraph* graph = buildFamilyGraph(chunkCount1, adjacentChunkPartition1, adjacentChunkIndices1); - graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); - - std::vector scratch; - scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount1)); - - uint32_t edges = graph->getEdgesCount(m_graph); - for (uint32_t node0 = 0; node0 < chunkCount1; node0++) - { - for (uint32_t i = adjacentChunkPartition1[node0]; i < adjacentChunkPartition1[node0 + 1]; i++) - { - if (graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, node0, adjacentChunkIndices1[i], m_graph)) - { - edges--; - EXPECT_EQ(edges, graph->getEdgesCount(m_graph)); - } - } - } - EXPECT_EQ(0, graph->getEdgesCount(m_graph)); - - EXPECT_EQ(12, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); - - for (uint32_t node0 = 0; node0 < chunkCount1; node0++) - { - EXPECT_EQ(node0, graph->getIslandIds()[node0]); - } -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "BlastBaseTest.h" + +#include "NvBlastSupportGraph.h" +#include "NvBlastFamilyGraph.h" +#include "NvBlastAssert.h" +#include "NvBlastIndexFns.h" + +#include +#include +#include +#include +#include + + +// ==================================================================================================================== +// HELPERS +// ==================================================================================================================== + +::testing::AssertionResult VectorMatch(const std::vector& actual, const uint32_t* expected, uint32_t size) +{ + for (size_t i(0); i < size; ++i) + { + if (expected[i] != actual[i]) + { + testing::Message msg; + msg << "array[" << i + << "] (" << actual[i] << ") != expected[" << i + << "] (" << expected[i] << ")"; + return (::testing::AssertionFailure(msg));; + } + } + + return ::testing::AssertionSuccess(); +} + +#define VECTOR_MATCH(actual, ...) \ +{ \ + const uint32_t arr[] = { __VA_ARGS__ }; \ + const uint32_t size = (sizeof(arr) / sizeof(arr[0])); \ + EXPECT_EQ(size, actual.size()); \ + EXPECT_TRUE(VectorMatch(actual, arr, size)); \ +} + + +// ==================================================================================================================== +// TEST CLASS +// ==================================================================================================================== + +using namespace Nv::Blast; + +template +class FamilyGraphTest : public BlastBaseTest < FailLevel, Verbosity > +{ +public: + FamilyGraphTest() + { + } + +protected: + FamilyGraph* buildFamilyGraph(uint32_t chunkCount, const uint32_t* adjacentChunkPartition, const uint32_t* adjacentChunkIndices) + { + NVBLAST_ASSERT(m_memoryBlock.size() == 0); // can't build twice per test + + // Fill SupportGraph with data: + NvBlastCreateOffsetStart(sizeof(SupportGraph)); + const size_t NvBlastCreateOffsetAlign16(chunkIndicesOffset, chunkCount*sizeof(uint32_t)); + const size_t NvBlastCreateOffsetAlign16(adjacencyPartitionOffset, (chunkCount + 1)*sizeof(uint32_t)); + const size_t NvBlastCreateOffsetAlign16(adjacentNodeIndicesOffset, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); + const size_t NvBlastCreateOffsetAlign16(adjacentBondIndicesOffset, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); + const size_t graphDataSize = NvBlastCreateOffsetEndAlign16(); + m_graphMemory.resize(graphDataSize); + m_graph = reinterpret_cast(m_graphMemory.data()); + + m_graph->m_nodeCount = chunkCount; + m_graph->m_chunkIndicesOffset = static_cast(chunkIndicesOffset); + m_graph->m_adjacencyPartitionOffset = static_cast(adjacencyPartitionOffset); + m_graph->m_adjacentNodeIndicesOffset = static_cast(adjacentNodeIndicesOffset); + m_graph->m_adjacentBondIndicesOffset = static_cast(adjacentBondIndicesOffset); + + memcpy(m_graph->getAdjacencyPartition(), adjacentChunkPartition, (chunkCount + 1) * sizeof(uint32_t)); + memcpy(m_graph->getAdjacentNodeIndices(), adjacentChunkIndices, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); + + // fill bondIndices by incrementing bondIndex and putting same bondIndex in mirror bond index for (n0, n1) == (n1, n0) + memset(m_graph->getAdjacentBondIndices(), (uint32_t)-1, adjacentChunkPartition[chunkCount] * sizeof(uint32_t)); + uint32_t bondIndex = 0; + for (uint32_t chunk0 = 0; chunk0 < m_graph->m_nodeCount; chunk0++) + { + for (uint32_t i = m_graph->getAdjacencyPartition()[chunk0]; i < m_graph->getAdjacencyPartition()[chunk0 + 1]; i++) + { + if (m_graph->getAdjacentBondIndices()[i] == (uint32_t)-1) + { + m_graph->getAdjacentBondIndices()[i] = bondIndex; + + uint32_t chunk1 = m_graph->getAdjacentNodeIndices()[i]; + for (uint32_t j = m_graph->getAdjacencyPartition()[chunk1]; j < m_graph->getAdjacencyPartition()[chunk1 + 1]; j++) + { + if (m_graph->getAdjacentNodeIndices()[j] == chunk0) + { + m_graph->getAdjacentBondIndices()[j] = bondIndex; + } + } + bondIndex++; + } + } + } + + // reserve memory for family graph and asset pointer + uint32_t familyGraphMemorySize = (uint32_t)FamilyGraph::requiredMemorySize(m_graph->m_nodeCount, bondIndex); + m_memoryBlock.resize(familyGraphMemorySize); + // placement new family graph + FamilyGraph* familyGraph = new(m_memoryBlock.data()) FamilyGraph(m_graph); + + return familyGraph; + } + + struct IslandInfo + { + std::vector nodes; + }; + + /** + Function to gather islands info for tests and debug purposes + Returned islands sorted by nodes counts. Island nodes also sorted by NodeIndex. + */ + void getIslandsInfo(const FamilyGraph& graph, std::vector& info) + { + IslandId* islandIds = graph.getIslandIds(); + + std::map islandMap; + + for (NodeIndex n = 0; n < m_graph->m_nodeCount; n++) + { + EXPECT_TRUE(islandIds[n] != invalidIndex()); + IslandId islandId = islandIds[n]; + if (islandMap.find(islandId) == islandMap.end()) + { + IslandInfo info; + info.nodes.push_back(n); + islandMap[islandId] = info; + } + else + { + islandMap[islandId].nodes.push_back(n); + } + } + + for (auto it = islandMap.begin(); it != islandMap.end(); ++it) + { + std::sort(it->second.nodes.begin(), it->second.nodes.end()); + info.push_back(it->second); + } + + // sort islands by size ascending + std::sort(info.begin(), info.end(), [](const IslandInfo& i0, const IslandInfo& i1) -> bool + { + size_t s0 = i0.nodes.size(); + size_t s1 = i1.nodes.size(); + if (s0 == s1 && s0 > 0) + { + s0 = i0.nodes[0]; + s1 = i1.nodes[0]; + } + return s0 < s1; + }); + } + + static const uint32_t DEFAULT_ACTOR_INDEX = 0; + + SupportGraph* m_graph; + std::vector m_graphMemory; + std::vector m_memoryBlock; +}; + +typedef FamilyGraphTest FamilyGraphTestAllowWarnings; +typedef FamilyGraphTest FamilyGraphTestStrict; + + +// ==================================================================================================================== +// GRAPH DATA +// ==================================================================================================================== + +// Graph 0: +// +// 0 -- 1 -- 2 -- 3 +// | | | | +// | | | | +// 4 -- 5 6 -- 7 +// +const uint32_t chunkCount0 = 8; +const uint32_t adjacentChunkPartition0[] = { 0, 2, 5, 8, 10, 12, 14, 16, 18 }; +const uint32_t adjacentChunkIndices0[] = { /*0*/ 1, 4, /*1*/ 0, 2, 5, /*2*/ 1, 3, 6, /*3*/ 2, 7, /*4*/ 0, 5, /*5*/ 1, 4, /*6*/ 2, 7, /*7*/ 3, 6 }; + + +// Graph 1: +// +// 0 -- 1 -- 2 -- 3 +// | | | | +// 4 -- 5 -- 6 -- 7 +// | | | | +// 8 -- 9 -- 10-- 11 +// +const uint32_t chunkCount1 = 12; +const uint32_t adjacentChunkPartition1[] = { 0, 2, 5, 8, 10, 13, 17, 21, 24, 26, 29, 32, 34 }; +const uint32_t adjacentChunkIndices1[] = { /*0*/ 1, 4, /*1*/ 0, 2, 5, /*2*/ 1, 3, 6, /*3*/ 2, 7, /*4*/ 0, 5, 8, /*5*/ 1, 4, 6, 9, /*6*/ 2, 5, 7, 10, + /*7*/ 3, 6, 11, /*8*/ 4, 9, /*9*/ 5, 8, 10, /*10*/ 6, 9, 11, /*11*/ 7, 10 }; + + +// ==================================================================================================================== +// TESTS +// ==================================================================================================================== + +TEST_F(FamilyGraphTestStrict, Graph0FindIslands0) +{ + FamilyGraph* graph = buildFamilyGraph(chunkCount0, adjacentChunkPartition0, adjacentChunkIndices0); + graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); + + std::vector scratch; + scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount0)); + + EXPECT_EQ(9, graph->getEdgesCount(m_graph)); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); + EXPECT_EQ(8, graph->getEdgesCount(m_graph)); + EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 2, m_graph); + EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + + std::vector info; + getIslandsInfo(*graph, info); + EXPECT_EQ(2, info.size()); + VECTOR_MATCH(info[0].nodes, 0, 1, 4, 5); + VECTOR_MATCH(info[1].nodes, 2, 3, 6, 7); +} + +TEST_F(FamilyGraphTestStrict, Graph0FindIslands1) +{ + FamilyGraph* graph = buildFamilyGraph(chunkCount0, adjacentChunkPartition0, adjacentChunkIndices0); + graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); + + std::vector scratch; + scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount0)); + + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 4, 5, m_graph); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 2, m_graph); + EXPECT_EQ(6, graph->getEdgesCount(m_graph)); + EXPECT_EQ(3, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + + std::vector info; + getIslandsInfo(*graph, info); + EXPECT_EQ(3, info.size()); + VECTOR_MATCH(info[0].nodes, 4); + VECTOR_MATCH(info[1].nodes, 0, 1, 5); + VECTOR_MATCH(info[2].nodes, 2, 3, 6, 7); +} + +TEST_F(FamilyGraphTestStrict, Graph0FindIslandsDifferentActors) +{ + const uint32_t ACTOR_0_INDEX = 5; + const uint32_t ACTOR_1_INDEX = 2; + + FamilyGraph* graph = buildFamilyGraph(chunkCount0, adjacentChunkPartition0, adjacentChunkIndices0); + graph->initialize(ACTOR_0_INDEX, m_graph); + + std::vector scratch; + scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount0)); + + EXPECT_EQ(0, graph->findIslands(ACTOR_1_INDEX, scratch.data(), m_graph)); + EXPECT_EQ(1, graph->findIslands(ACTOR_0_INDEX, scratch.data(), m_graph)); + + graph->notifyEdgeRemoved(ACTOR_0_INDEX, 2, 1, m_graph); + EXPECT_EQ(8, graph->getEdgesCount(m_graph)); + + EXPECT_EQ(1, graph->findIslands(ACTOR_0_INDEX, scratch.data(), m_graph)); + + graph->notifyEdgeRemoved(ACTOR_1_INDEX, 2, 6, m_graph); + graph->notifyEdgeRemoved(ACTOR_1_INDEX, 7, 3, m_graph); + EXPECT_EQ(1, graph->findIslands(ACTOR_1_INDEX, scratch.data(), m_graph)); + + graph->notifyEdgeRemoved(ACTOR_0_INDEX, 0, 1, m_graph); + graph->notifyEdgeRemoved(ACTOR_0_INDEX, 4, 5, m_graph); + EXPECT_EQ(1, graph->findIslands(ACTOR_0_INDEX, scratch.data(), m_graph)); + + + std::vector info; + getIslandsInfo(*graph, info); + EXPECT_EQ(4, info.size()); + VECTOR_MATCH(info[0].nodes, 0, 4); + VECTOR_MATCH(info[1].nodes, 1, 5); + VECTOR_MATCH(info[2].nodes, 2, 3); + VECTOR_MATCH(info[3].nodes, 6, 7); +} + +TEST_F(FamilyGraphTestStrict, Graph1FindIslands0) +{ + FamilyGraph* graph = buildFamilyGraph(chunkCount1, adjacentChunkPartition1, adjacentChunkIndices1); + graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); + + std::vector scratch; + scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount1)); + + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 5, m_graph); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 2, 6, m_graph); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 3, 7, m_graph); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 5, 6, m_graph); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 9, 10, m_graph); + EXPECT_EQ(11, graph->getEdgesCount(m_graph)); + EXPECT_EQ(3, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + + std::vector info; + getIslandsInfo(*graph, info); + EXPECT_EQ(3, info.size()); + + VECTOR_MATCH(info[0].nodes, 0, 1, 2, 3); + VECTOR_MATCH(info[1].nodes, 4, 5, 8, 9); + VECTOR_MATCH(info[2].nodes, 6, 7, 10, 11); +} + +TEST_F(FamilyGraphTestStrict, Graph1FindIslands1) +{ + FamilyGraph* graph = buildFamilyGraph(chunkCount1, adjacentChunkPartition1, adjacentChunkIndices1); + graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); + + std::vector scratch; + scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount1)); + + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 0, 4, m_graph); + EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 1, 5, m_graph); + EXPECT_EQ(0, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 2, 6, m_graph); + EXPECT_EQ(0, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 3, 7, m_graph); + EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 5, 6, m_graph); + EXPECT_EQ(0, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, 9, 10, m_graph); + EXPECT_EQ(1, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + + std::vector info; + getIslandsInfo(*graph, info); + EXPECT_EQ(3, info.size()); + VECTOR_MATCH(info[0].nodes, 0, 1, 2, 3); + VECTOR_MATCH(info[1].nodes, 4, 5, 8, 9); + VECTOR_MATCH(info[2].nodes, 6, 7, 10, 11); +} + +TEST_F(FamilyGraphTestStrict, Graph1FindIslandsRemoveAllEdges) +{ + FamilyGraph* graph = buildFamilyGraph(chunkCount1, adjacentChunkPartition1, adjacentChunkIndices1); + graph->initialize(DEFAULT_ACTOR_INDEX, m_graph); + + std::vector scratch; + scratch.resize((size_t)FamilyGraph::findIslandsRequiredScratch(chunkCount1)); + + uint32_t edges = graph->getEdgesCount(m_graph); + for (uint32_t node0 = 0; node0 < chunkCount1; node0++) + { + for (uint32_t i = adjacentChunkPartition1[node0]; i < adjacentChunkPartition1[node0 + 1]; i++) + { + if (graph->notifyEdgeRemoved(DEFAULT_ACTOR_INDEX, node0, adjacentChunkIndices1[i], m_graph)) + { + edges--; + EXPECT_EQ(edges, graph->getEdgesCount(m_graph)); + } + } + } + EXPECT_EQ(0, graph->getEdgesCount(m_graph)); + + EXPECT_EQ(12, graph->findIslands(DEFAULT_ACTOR_INDEX, scratch.data(), m_graph)); + + for (uint32_t node0 = 0; node0 < chunkCount1; node0++) + { + EXPECT_EQ(node0, graph->getIslandIds()[node0]); + } +} diff --git a/test/src/unit/MultithreadingTests.cpp b/test/src/unit/MultithreadingTests.cpp old mode 100644 new mode 100755 index cd5f936..f3fc8fd --- a/test/src/unit/MultithreadingTests.cpp +++ b/test/src/unit/MultithreadingTests.cpp @@ -1,420 +1,420 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBaseTest.h" -#include "AssetGenerator.h" - -#include -#include -#include "TaskDispatcher.h" - -#include "NvBlastActor.h" -#include "NvBlastExtDamageShaders.h" - -#if NV_XBOXONE -#undef min -#undef max -#endif - -typedef std::function ActorTestFunction; -typedef std::function&, NvBlastLog)> PostDamageTestFunction; - - -static void blast(std::set& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) -{ - std::vector chunkEvents; /* num lower-support chunks + bonds */ - std::vector bondEvents; /* num lower-support chunks + bonds */ - chunkEvents.resize(testAsset->solverChunks.size()); - bondEvents.resize(testAsset->solverBonds.size()); - - NvBlastFractureBuffers events = { static_cast(bondEvents.size()), static_cast(chunkEvents.size()), bondEvents.data(), chunkEvents.data() }; - - std::vector scratch(chunkEvents.size() + bondEvents.size(), 0.0f); - - std::vector splitScratch; - std::vector newActorsBuffer(testAsset->solverChunks.size()); - - NvBlastExtRadialDamageDesc damage = { - compressiveDamage, - { localPos.x, localPos.y, localPos.z }, - minRadius, - maxRadius - }; - - NvBlastExtProgramParams programParams = - { - &damage, - nullptr - }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - nullptr - }; - - size_t totalNewActorsCount = 0; - for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) - { - NvBlastActor* actor = *k; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr); - NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr); - - bool removeActor = false; - - if (events.bondFractureCount + events.chunkFractureCount > 0) - { - NvBlastActorSplitEvent splitEvent; - splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount]; - uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount); - - splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); - const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr); - totalNewActorsCount += newActorsCount; - removeActor = splitEvent.deletedActor != NULL; - } - - if (removeActor) - { - k = actorsToDamage.erase(k); - } - else - { - ++k; - } - } - - for (size_t i = 0; i < totalNewActorsCount; ++i) - { - actorsToDamage.insert(newActorsBuffer[i]); - } -} - - -template -class MultithreadingTest : public BlastBaseTest -{ -public: - MultithreadingTest() - { - - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - BlastBaseTest::alignedFree(mem); - } - - static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog) - { - const Nv::Blast::Asset& asset = *actor.getAsset(); - const NvBlastChunk* chunks = asset.getChunks(); - - if (actor.isSubSupportChunk()) - { - EXPECT_EQ(1, actor.getVisibleChunkCount()); - - const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor); - - EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex); - - // Make sure the visible chunk is subsupport - // Array of support flags - std::vector isSupport(asset.m_chunkCount, false); - for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i) - { - isSupport[asset.m_graph.getChunkIndices()[i]] = true; - } - - // Climb hierarchy to find support chunk - uint32_t chunkIndex = firstVisibleChunkIndex; - while (chunkIndex != Nv::Blast::invalidIndex()) - { - if (isSupport[chunkIndex]) - { - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - - EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); - } - else - { - // Array of visibility flags - std::vector isVisible(asset.m_chunkCount, false); - for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i) - { - isVisible[(uint32_t)i] = true; - } - - // Mark visible nodes representing graph chunks - std::vector visibleChunkFound(asset.m_chunkCount, false); - - // Make sure every graph chunk is represented by a visible chunk - for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i) - { - const uint32_t graphNodeIndex = (uint32_t)i; - uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex]; - // Climb hierarchy to find visible chunk - while (chunkIndex != Nv::Blast::invalidIndex()) - { - // Check that chunk owners are accurate - EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]); - if (isVisible[chunkIndex]) - { - visibleChunkFound[chunkIndex] = true; - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); - } - - // Check that all visible chunks are accounted for - for (uint32_t i = 0; i < asset.m_chunkCount; ++i) - { - EXPECT_EQ(visibleChunkFound[i], isVisible[i]); - } - - // Make sure that, if all siblings are intact, they are invisible - for (uint32_t i = 0; i < asset.m_chunkCount; ++i) - { - bool allIntact = true; - bool noneVisible = true; - if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport - { - for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j) - { - allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex(); - noneVisible = noneVisible && !isVisible[j]; - } - EXPECT_TRUE(!allIntact || noneVisible); - } - } - } - } - - class DamageActorTask : public TaskDispatcher::Task - { - public: - DamageActorTask(NvBlastActor* actor, GeneratorAsset* asset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage, ActorTestFunction testFunction) - : m_asset(asset) - , m_localPos(localPos) - , m_minRadius(minRadius) - , m_maxRadius(maxRadius) - , m_compressiveDamage(compressiveDamage) - , m_testFunction(testFunction) - { - m_actors.insert(actor); - } - - virtual void process() - { - blast(m_actors, m_asset, m_localPos, m_minRadius, m_maxRadius, m_compressiveDamage); - - // Test individual actors - if (m_testFunction != nullptr) - { - for (std::set::iterator k = m_actors.begin(); k != m_actors.end(); ++k) - { - m_testFunction(*static_cast(*k), messageLog); - } - } - } - - const std::set& getResult() const { return m_actors; } - - private: - std::set m_actors; - GeneratorAsset* m_asset; - GeneratorAsset::Vec3 m_localPos; - float m_minRadius; - float m_maxRadius; - float m_compressiveDamage; - ActorTestFunction m_testFunction; - - std::vector m_resultActors; - }; - - void damageLeafSupportActorsParallelized - ( - uint32_t assetCount, - uint32_t minChunkCount, - uint32_t damageCount, - uint32_t threadCount, - ActorTestFunction actorTestFunction, - PostDamageTestFunction postDamageTestFunction - ) - { - const float relativeDamageRadius = 0.05f; - const float compressiveDamage = 1.0f; - - srand(0); - - std::cout << "Asset # (out of " << assetCount << "): "; - for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) - { - std::cout << assetNum + 1 << ".. "; - CubeAssetGenerator::Settings settings; - settings.extents = GeneratorAsset::Vec3(1, 1, 1); - CubeAssetGenerator::DepthInfo depthInfo; - depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); - depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; - settings.depths.push_back(depthInfo); - uint32_t chunkCount = 1; - while (chunkCount < minChunkCount) - { - uint32_t chunkMul; - do - { - depthInfo.slicesPerAxis = GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); - chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); - } while (chunkMul == 1); - chunkCount *= chunkMul; - settings.depths.push_back(depthInfo); - settings.extents = settings.extents * depthInfo.slicesPerAxis; - } - settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support - - // Make largest direction unit size - settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); - - // Create asset - GeneratorAsset testAsset; - CubeAssetGenerator::generate(testAsset, settings); - - NvBlastAssetDesc desc; - desc.chunkDescs = &testAsset.solverChunks[0]; - desc.chunkCount = (uint32_t)testAsset.solverChunks.size(); - desc.bondDescs = testAsset.solverBonds.data(); - desc.bondCount = (uint32_t)testAsset.solverBonds.size(); - - std::vector scratch; - scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); - void* mem = alloc(NvBlastGetAssetMemorySize(&desc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); // Using zeroingAlloc in case actorTest compares memory blocks - scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // Run parallelized damage through TaskDispatcher - std::set resultActors; - { - uint32_t damageNum = 0; - - // create DamageActorTask and it to dispatcher helper function - auto addDamageTaskFunction = [&](TaskDispatcher& dispatcher, NvBlastActor* actor) - { - GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); - auto newTask = std::unique_ptr(new DamageActorTask(actor, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage, actorTestFunction)); - dispatcher.addTask(std::move(newTask)); - }; - - // on task finished function for dispatcher (main thread) - TaskDispatcher::OnTaskFinishedFunction onTaskFinishedFunction = [&](TaskDispatcher& dispatcher, std::unique_ptr task) { - const DamageActorTask* damageTask = static_cast(task.get()); - const std::set& actors = damageTask->getResult(); - for (NvBlastActor* actor : actors) - { - if (damageNum >= damageCount) - { - resultActors.insert(actor); - } - else - { - damageNum++; - addDamageTaskFunction(dispatcher, actor); - } - } - }; - - // create dispatcher, add first task and run - TaskDispatcher dispatcher(threadCount, onTaskFinishedFunction); - addDamageTaskFunction(dispatcher, actor); - dispatcher.process(); - } - - // Test fractured actor set - if (postDamageTestFunction) - { - std::vector actorArray(resultActors.begin(), resultActors.end()); - postDamageTestFunction(actorArray, messageLog); - } - - // Release remaining actors - for (std::set::iterator k = resultActors.begin(); k != resultActors.end(); ++k) - { - NvBlastActorDeactivate(*k, messageLog); - } - resultActors.clear(); - - const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog); - EXPECT_TRUE(actorCount == 0); - - free(family); - - // Release asset data - free(asset); - } - std::cout << "done.\n"; - } -}; - - -// Specializations -typedef MultithreadingTest MultithreadingTestAllowWarnings; -typedef MultithreadingTest MultithreadingTestStrict; - - -TEST_F(MultithreadingTestStrict, MultithreadingTestDamageLeafSupportActorsTestVisibility) -{ - damageLeafSupportActorsParallelized(1, 1000, 50, 4, testActorVisibleChunks, nullptr); -} - -TEST_F(MultithreadingTestStrict, MultithreadingTestDamageLeafSupportActors) -{ - damageLeafSupportActorsParallelized(1, 3000, 1000, 4, nullptr, nullptr); -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "BlastBaseTest.h" +#include "AssetGenerator.h" + +#include +#include +#include "TaskDispatcher.h" + +#include "NvBlastActor.h" +#include "NvBlastExtDamageShaders.h" + +#if NV_XBOXONE +#undef min +#undef max +#endif + +typedef std::function ActorTestFunction; +typedef std::function&, NvBlastLog)> PostDamageTestFunction; + + +static void blast(std::set& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) +{ + std::vector chunkEvents; /* num lower-support chunks + bonds */ + std::vector bondEvents; /* num lower-support chunks + bonds */ + chunkEvents.resize(testAsset->solverChunks.size()); + bondEvents.resize(testAsset->solverBonds.size()); + + NvBlastFractureBuffers events = { static_cast(bondEvents.size()), static_cast(chunkEvents.size()), bondEvents.data(), chunkEvents.data() }; + + std::vector scratch(chunkEvents.size() + bondEvents.size(), 0.0f); + + std::vector splitScratch; + std::vector newActorsBuffer(testAsset->solverChunks.size()); + + NvBlastExtRadialDamageDesc damage = { + compressiveDamage, + { localPos.x, localPos.y, localPos.z }, + minRadius, + maxRadius + }; + + NvBlastExtProgramParams programParams = + { + &damage, + nullptr + }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + size_t totalNewActorsCount = 0; + for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) + { + NvBlastActor* actor = *k; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr); + NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr); + + bool removeActor = false; + + if (events.bondFractureCount + events.chunkFractureCount > 0) + { + NvBlastActorSplitEvent splitEvent; + splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount]; + uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount); + + splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); + const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr); + totalNewActorsCount += newActorsCount; + removeActor = splitEvent.deletedActor != NULL; + } + + if (removeActor) + { + k = actorsToDamage.erase(k); + } + else + { + ++k; + } + } + + for (size_t i = 0; i < totalNewActorsCount; ++i) + { + actorsToDamage.insert(newActorsBuffer[i]); + } +} + + +template +class MultithreadingTest : public BlastBaseTest +{ +public: + MultithreadingTest() + { + + } + + static void messageLog(int type, const char* msg, const char* file, int line) + { + BlastBaseTest::messageLog(type, msg, file, line); + } + + static void* alloc(size_t size) + { + return BlastBaseTest::alignedZeroedAlloc(size); + } + + static void free(void* mem) + { + BlastBaseTest::alignedFree(mem); + } + + static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog) + { + const Nv::Blast::Asset& asset = *actor.getAsset(); + const NvBlastChunk* chunks = asset.getChunks(); + + if (actor.isSubSupportChunk()) + { + EXPECT_EQ(1, actor.getVisibleChunkCount()); + + const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor); + + EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex); + + // Make sure the visible chunk is subsupport + // Array of support flags + std::vector isSupport(asset.m_chunkCount, false); + for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i) + { + isSupport[asset.m_graph.getChunkIndices()[i]] = true; + } + + // Climb hierarchy to find support chunk + uint32_t chunkIndex = firstVisibleChunkIndex; + while (chunkIndex != Nv::Blast::invalidIndex()) + { + if (isSupport[chunkIndex]) + { + break; + } + chunkIndex = chunks[chunkIndex].parentChunkIndex; + } + + EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); + } + else + { + // Array of visibility flags + std::vector isVisible(asset.m_chunkCount, false); + for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i) + { + isVisible[(uint32_t)i] = true; + } + + // Mark visible nodes representing graph chunks + std::vector visibleChunkFound(asset.m_chunkCount, false); + + // Make sure every graph chunk is represented by a visible chunk + for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i) + { + const uint32_t graphNodeIndex = (uint32_t)i; + uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex]; + // Climb hierarchy to find visible chunk + while (chunkIndex != Nv::Blast::invalidIndex()) + { + // Check that chunk owners are accurate + EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]); + if (isVisible[chunkIndex]) + { + visibleChunkFound[chunkIndex] = true; + break; + } + chunkIndex = chunks[chunkIndex].parentChunkIndex; + } + EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); + } + + // Check that all visible chunks are accounted for + for (uint32_t i = 0; i < asset.m_chunkCount; ++i) + { + EXPECT_EQ(visibleChunkFound[i], isVisible[i]); + } + + // Make sure that, if all siblings are intact, they are invisible + for (uint32_t i = 0; i < asset.m_chunkCount; ++i) + { + bool allIntact = true; + bool noneVisible = true; + if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport + { + for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j) + { + allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex(); + noneVisible = noneVisible && !isVisible[j]; + } + EXPECT_TRUE(!allIntact || noneVisible); + } + } + } + } + + class DamageActorTask : public TaskDispatcher::Task + { + public: + DamageActorTask(NvBlastActor* actor, GeneratorAsset* asset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage, ActorTestFunction testFunction) + : m_asset(asset) + , m_localPos(localPos) + , m_minRadius(minRadius) + , m_maxRadius(maxRadius) + , m_compressiveDamage(compressiveDamage) + , m_testFunction(testFunction) + { + m_actors.insert(actor); + } + + virtual void process() + { + blast(m_actors, m_asset, m_localPos, m_minRadius, m_maxRadius, m_compressiveDamage); + + // Test individual actors + if (m_testFunction != nullptr) + { + for (std::set::iterator k = m_actors.begin(); k != m_actors.end(); ++k) + { + m_testFunction(*static_cast(*k), messageLog); + } + } + } + + const std::set& getResult() const { return m_actors; } + + private: + std::set m_actors; + GeneratorAsset* m_asset; + GeneratorAsset::Vec3 m_localPos; + float m_minRadius; + float m_maxRadius; + float m_compressiveDamage; + ActorTestFunction m_testFunction; + + std::vector m_resultActors; + }; + + void damageLeafSupportActorsParallelized + ( + uint32_t assetCount, + uint32_t minChunkCount, + uint32_t damageCount, + uint32_t threadCount, + ActorTestFunction actorTestFunction, + PostDamageTestFunction postDamageTestFunction + ) + { + const float relativeDamageRadius = 0.05f; + const float compressiveDamage = 1.0f; + + srand(0); + + std::cout << "Asset # (out of " << assetCount << "): "; + for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) + { + std::cout << assetNum + 1 << ".. "; + CubeAssetGenerator::Settings settings; + settings.extents = GeneratorAsset::Vec3(1, 1, 1); + CubeAssetGenerator::DepthInfo depthInfo; + depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); + depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; + settings.depths.push_back(depthInfo); + uint32_t chunkCount = 1; + while (chunkCount < minChunkCount) + { + uint32_t chunkMul; + do + { + depthInfo.slicesPerAxis = GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); + chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); + } while (chunkMul == 1); + chunkCount *= chunkMul; + settings.depths.push_back(depthInfo); + settings.extents = settings.extents * depthInfo.slicesPerAxis; + } + settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support + + // Make largest direction unit size + settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); + + // Create asset + GeneratorAsset testAsset; + CubeAssetGenerator::generate(testAsset, settings); + + NvBlastAssetDesc desc; + desc.chunkDescs = &testAsset.solverChunks[0]; + desc.chunkCount = (uint32_t)testAsset.solverChunks.size(); + desc.bondDescs = testAsset.solverBonds.data(); + desc.bondCount = (uint32_t)testAsset.solverBonds.size(); + + std::vector scratch; + scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, messageLog)); + void* mem = alloc(NvBlastGetAssetMemorySize(&desc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); // Using zeroingAlloc in case actorTest compares memory blocks + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // Run parallelized damage through TaskDispatcher + std::set resultActors; + { + uint32_t damageNum = 0; + + // create DamageActorTask and it to dispatcher helper function + auto addDamageTaskFunction = [&](TaskDispatcher& dispatcher, NvBlastActor* actor) + { + GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); + auto newTask = std::unique_ptr(new DamageActorTask(actor, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage, actorTestFunction)); + dispatcher.addTask(std::move(newTask)); + }; + + // on task finished function for dispatcher (main thread) + TaskDispatcher::OnTaskFinishedFunction onTaskFinishedFunction = [&](TaskDispatcher& dispatcher, std::unique_ptr task) { + const DamageActorTask* damageTask = static_cast(task.get()); + const std::set& actors = damageTask->getResult(); + for (NvBlastActor* actor : actors) + { + if (damageNum >= damageCount) + { + resultActors.insert(actor); + } + else + { + damageNum++; + addDamageTaskFunction(dispatcher, actor); + } + } + }; + + // create dispatcher, add first task and run + TaskDispatcher dispatcher(threadCount, onTaskFinishedFunction); + addDamageTaskFunction(dispatcher, actor); + dispatcher.process(); + } + + // Test fractured actor set + if (postDamageTestFunction) + { + std::vector actorArray(resultActors.begin(), resultActors.end()); + postDamageTestFunction(actorArray, messageLog); + } + + // Release remaining actors + for (std::set::iterator k = resultActors.begin(); k != resultActors.end(); ++k) + { + NvBlastActorDeactivate(*k, messageLog); + } + resultActors.clear(); + + const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog); + EXPECT_TRUE(actorCount == 0); + + free(family); + + // Release asset data + free(asset); + } + std::cout << "done.\n"; + } +}; + + +// Specializations +typedef MultithreadingTest MultithreadingTestAllowWarnings; +typedef MultithreadingTest MultithreadingTestStrict; + + +TEST_F(MultithreadingTestStrict, MultithreadingTestDamageLeafSupportActorsTestVisibility) +{ + damageLeafSupportActorsParallelized(1, 1000, 50, 4, testActorVisibleChunks, nullptr); +} + +TEST_F(MultithreadingTestStrict, MultithreadingTestDamageLeafSupportActors) +{ + damageLeafSupportActorsParallelized(1, 3000, 1000, 4, nullptr, nullptr); +} diff --git a/test/src/unit/SyncTests.cpp b/test/src/unit/SyncTests.cpp old mode 100644 new mode 100755 index e322202..30c2bf4 --- a/test/src/unit/SyncTests.cpp +++ b/test/src/unit/SyncTests.cpp @@ -1,344 +1,344 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "TkBaseTest.h" - -#include "NvBlastExtSync.h" -#include "NvBlastTkEvent.h" - -#include - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// ExtSync Tests -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class Base -{ -public: - Base(TkTestStrict* test) : m_test(test) - { - - } - - void run(std::stringstream& finalState) - { - //////// initial setup //////// - - m_test->createTestAssets(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - TkGroupDesc gdesc; - gdesc.workerCount = m_test->m_taskman->getCpuDispatcher()->getWorkerCount(); - m_group = fwk->createGroup(gdesc); - EXPECT_TRUE(m_group != nullptr); - - TkActorDesc adesc(m_test->testAssets[0]); - - NvBlastID id; - - TkActor* actor0 = fwk->createActor(adesc); - EXPECT_TRUE(actor0 != nullptr); - families[0] = &actor0->getFamily(); - memcpy(id.data, "Mumble Jumble Bumble", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) - families[0]->setID(id); - m_group->addActor(*actor0); - - TkActor* actor1 = fwk->createActor(adesc); - EXPECT_TRUE(actor1 != nullptr); - families[1] = &actor1->getFamily(); - memcpy(id.data, "buzzkillerdiller", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) - families[1]->setID(id); - m_group->addActor(*actor1); - - m_test->m_groupTM->setGroup(m_group); - - //////// server/client specific impl //////// - - impl(); - - - //////// write out framework final state //////// - - finalState.clear(); - for (auto family : families) - { - std::vector actors(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - for (auto actor : actors) - { - finalState << actor->getVisibleChunkCount(); - finalState << actor->getGraphNodeCount(); - std::vector chunkIndices(actor->getGraphNodeCount()); - actor->getVisibleChunkIndices(chunkIndices.data(), (uint32_t)chunkIndices.size()); - - for (uint32_t chunkIndex : chunkIndices) - finalState << chunkIndex; - const float* bondHealths = actor->getBondHealths(); - for (uint32_t i = 0; i < actor->getAsset()->getBondCount(); ++i) - finalState << bondHealths[i]; - } - } - - - //////// release //////// - - m_group->release(); - - for (auto family : families) - { - family->release(); - } - - m_test->releaseTestAssets(); - } - -protected: - virtual void impl() = 0; - - TkTestStrict* m_test; - TkGroup* m_group; - TkFamily* families[2]; -}; - - -class Server : public Base -{ -public: - Server(TkTestStrict* test, std::vector& syncBuffer) : Base(test), m_syncBuffer(syncBuffer) {} - -protected: - virtual void impl() override - { - // create sync ext - ExtSync* sync = ExtSync::create(); - - // add sync as listener to family #1 - families[1]->addListener(*sync); - - // damage params - CSParams cs0(1, 0.0f); - NvBlastExtProgramParams csParams0 = { &cs0, nullptr }; - NvBlastExtRadialDamageDesc radialDamage0 = m_test->getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialParams0 = { &radialDamage0, nullptr }; - NvBlastExtRadialDamageDesc radialDamage1 = m_test->getRadialDamageDesc(0, 0, 0, 10.0f, 10.0f, 0.1f); - NvBlastExtProgramParams radialParams1 = { &radialDamage1, nullptr }; - - // damage family #0 (make it split) - { - TkActor* actor; - families[0]->getActors(&actor, 1); - actor->damage(m_test->getCubeSlicerProgram(), &csParams0); - } - - // process - m_test->m_groupTM->process(); - m_test->m_groupTM->wait(); - EXPECT_EQ(families[0]->getActorCount(), 2); - - // sync family #0 - sync->syncFamily(*families[0]); - - // add sync as listener to family #0 - families[0]->addListener(*sync); - - // damage family #0 (make it split fully) - { - TkActor* actor; - families[0]->getActors(&actor, 1, 1); - actor->damage(m_test->getFalloffProgram(), &radialParams0); - } - - - // damage family 1 (just damage bonds health) - { - TkActor* actor; - families[1]->getActors(&actor, 1); - NvBlastExtRadialDamageDesc radialDamage = m_test->getRadialDamageDesc(0, 0, 0, 10.0f, 10.0f, 0.1f); - actor->damage(m_test->getFalloffProgram(), &radialParams1); - } - - // process - m_test->m_groupTM->process(); - m_test->m_groupTM->wait(); - EXPECT_EQ(families[0]->getActorCount(), 5); - EXPECT_EQ(families[1]->getActorCount(), 1); - - // take sync buffer from sync - { - const ExtSyncEvent*const* buffer; - uint32_t size; - sync->acquireSyncBuffer(buffer, size); - - m_syncBuffer.resize(size); - for (size_t i = 0; i < size; ++i) - { - m_syncBuffer[i] = buffer[i]->clone(); - } - - sync->releaseSyncBuffer(); - } - - // - families[0]->removeListener(*sync); - families[1]->removeListener(*sync); - - // - sync->release(); - } - -private: - std::vector& m_syncBuffer; -}; - - -class Client : public Base, public TkEventListener -{ -public: - Client(TkTestStrict* test, std::vector& syncBuffer) : Base(test), m_syncBuffer(syncBuffer) {} - -protected: - - virtual void impl() override - { - ExtSync* sync = ExtSync::create(); - - // fill map - for (auto& family : families) - { - std::vector actors(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - auto& actorsSet = m_actorsPerFamily[family]; - for (auto actor : actors) - EXPECT_TRUE(actorsSet.insert(actor->getIndex()).second); - } - - // subscribe - for (auto& family : families) - { - family->addListener(*this); - } - - // apply sync buffer - sync->applySyncBuffer(*NvBlastTkFrameworkGet(), (const Nv::Blast::ExtSyncEvent**)m_syncBuffer.data(), static_cast(m_syncBuffer.size()), m_group); - - // check map - for (auto& family : families) - { - std::vector actors(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - std::set actorsSet; - for (auto actor : actors) - EXPECT_TRUE(actorsSet.insert(actor->getIndex()).second); - EXPECT_TRUE(m_actorsPerFamily[family] == actorsSet); - } - - // unsubscribe - for (auto& family : families) - { - family->removeListener(*this); - } - - m_test->m_groupTM->process(); - m_test->m_groupTM->wait(); - - sync->release(); - } - - // listen for Split event and update actors map - virtual void receive(const TkEvent* events, uint32_t eventCount) override - { - for (size_t i = 0; i < eventCount; ++i) - { - const TkEvent& e = events[i]; - switch (e.type) - { - case (TkEvent::Split) : - { - const TkSplitEvent* splitEvent = e.getPayload(); - auto& actorsSet = m_actorsPerFamily[splitEvent->parentData.family]; - if (!isInvalidIndex(splitEvent->parentData.index)) - { - EXPECT_EQ((size_t)1, actorsSet.erase(splitEvent->parentData.index)); - } - for (size_t i = 0; i < splitEvent->numChildren; ++i) - { - TkActor* a = splitEvent->children[i]; - EXPECT_TRUE(actorsSet.insert(a->getIndex()).second); - } - break; - } - case (TkEvent::FractureCommand) : - { - break; - } - case (TkEvent::JointUpdate) : - { - FAIL(); - break; - } - default: - break; - } - } - } - -private: - std::map> m_actorsPerFamily; - std::vector& m_syncBuffer; -}; - -TEST_F(TkTestStrict, SyncTest1) -{ - this->createFramework(); - - std::vector syncBuffer; - - std::stringstream serverFinalState; - { - Server s(this, syncBuffer); - s.run(serverFinalState); - } - EXPECT_TRUE(syncBuffer.size() > 0); - - std::stringstream clientFinalState; - { - Client c(this, syncBuffer); - c.run(clientFinalState); - } - - for (auto e : syncBuffer) - { - e->release(); - } - syncBuffer.clear(); - - EXPECT_EQ(serverFinalState.str(), clientFinalState.str()); - - this->releaseFramework(); -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "TkBaseTest.h" + +#include "NvBlastExtSync.h" +#include "NvBlastTkEvent.h" + +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ExtSync Tests +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class Base +{ +public: + Base(TkTestStrict* test) : m_test(test) + { + + } + + void run(std::stringstream& finalState) + { + //////// initial setup //////// + + m_test->createTestAssets(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + TkGroupDesc gdesc; + gdesc.workerCount = m_test->m_taskman->getCpuDispatcher()->getWorkerCount(); + m_group = fwk->createGroup(gdesc); + EXPECT_TRUE(m_group != nullptr); + + TkActorDesc adesc(m_test->testAssets[0]); + + NvBlastID id; + + TkActor* actor0 = fwk->createActor(adesc); + EXPECT_TRUE(actor0 != nullptr); + families[0] = &actor0->getFamily(); + memcpy(id.data, "Mumble Jumble Bumble", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) + families[0]->setID(id); + m_group->addActor(*actor0); + + TkActor* actor1 = fwk->createActor(adesc); + EXPECT_TRUE(actor1 != nullptr); + families[1] = &actor1->getFamily(); + memcpy(id.data, "buzzkillerdiller", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) + families[1]->setID(id); + m_group->addActor(*actor1); + + m_test->m_groupTM->setGroup(m_group); + + //////// server/client specific impl //////// + + impl(); + + + //////// write out framework final state //////// + + finalState.clear(); + for (auto family : families) + { + std::vector actors(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + for (auto actor : actors) + { + finalState << actor->getVisibleChunkCount(); + finalState << actor->getGraphNodeCount(); + std::vector chunkIndices(actor->getGraphNodeCount()); + actor->getVisibleChunkIndices(chunkIndices.data(), (uint32_t)chunkIndices.size()); + + for (uint32_t chunkIndex : chunkIndices) + finalState << chunkIndex; + const float* bondHealths = actor->getBondHealths(); + for (uint32_t i = 0; i < actor->getAsset()->getBondCount(); ++i) + finalState << bondHealths[i]; + } + } + + + //////// release //////// + + m_group->release(); + + for (auto family : families) + { + family->release(); + } + + m_test->releaseTestAssets(); + } + +protected: + virtual void impl() = 0; + + TkTestStrict* m_test; + TkGroup* m_group; + TkFamily* families[2]; +}; + + +class Server : public Base +{ +public: + Server(TkTestStrict* test, std::vector& syncBuffer) : Base(test), m_syncBuffer(syncBuffer) {} + +protected: + virtual void impl() override + { + // create sync ext + ExtSync* sync = ExtSync::create(); + + // add sync as listener to family #1 + families[1]->addListener(*sync); + + // damage params + CSParams cs0(1, 0.0f); + NvBlastExtProgramParams csParams0 = { &cs0, nullptr }; + NvBlastExtRadialDamageDesc radialDamage0 = m_test->getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialParams0 = { &radialDamage0, nullptr }; + NvBlastExtRadialDamageDesc radialDamage1 = m_test->getRadialDamageDesc(0, 0, 0, 10.0f, 10.0f, 0.1f); + NvBlastExtProgramParams radialParams1 = { &radialDamage1, nullptr }; + + // damage family #0 (make it split) + { + TkActor* actor; + families[0]->getActors(&actor, 1); + actor->damage(m_test->getCubeSlicerProgram(), &csParams0); + } + + // process + m_test->m_groupTM->process(); + m_test->m_groupTM->wait(); + EXPECT_EQ(families[0]->getActorCount(), 2); + + // sync family #0 + sync->syncFamily(*families[0]); + + // add sync as listener to family #0 + families[0]->addListener(*sync); + + // damage family #0 (make it split fully) + { + TkActor* actor; + families[0]->getActors(&actor, 1, 1); + actor->damage(m_test->getFalloffProgram(), &radialParams0); + } + + + // damage family 1 (just damage bonds health) + { + TkActor* actor; + families[1]->getActors(&actor, 1); + NvBlastExtRadialDamageDesc radialDamage = m_test->getRadialDamageDesc(0, 0, 0, 10.0f, 10.0f, 0.1f); + actor->damage(m_test->getFalloffProgram(), &radialParams1); + } + + // process + m_test->m_groupTM->process(); + m_test->m_groupTM->wait(); + EXPECT_EQ(families[0]->getActorCount(), 5); + EXPECT_EQ(families[1]->getActorCount(), 1); + + // take sync buffer from sync + { + const ExtSyncEvent*const* buffer; + uint32_t size; + sync->acquireSyncBuffer(buffer, size); + + m_syncBuffer.resize(size); + for (size_t i = 0; i < size; ++i) + { + m_syncBuffer[i] = buffer[i]->clone(); + } + + sync->releaseSyncBuffer(); + } + + // + families[0]->removeListener(*sync); + families[1]->removeListener(*sync); + + // + sync->release(); + } + +private: + std::vector& m_syncBuffer; +}; + + +class Client : public Base, public TkEventListener +{ +public: + Client(TkTestStrict* test, std::vector& syncBuffer) : Base(test), m_syncBuffer(syncBuffer) {} + +protected: + + virtual void impl() override + { + ExtSync* sync = ExtSync::create(); + + // fill map + for (auto& family : families) + { + std::vector actors(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + auto& actorsSet = m_actorsPerFamily[family]; + for (auto actor : actors) + EXPECT_TRUE(actorsSet.insert(actor->getIndex()).second); + } + + // subscribe + for (auto& family : families) + { + family->addListener(*this); + } + + // apply sync buffer + sync->applySyncBuffer(*NvBlastTkFrameworkGet(), (const Nv::Blast::ExtSyncEvent**)m_syncBuffer.data(), static_cast(m_syncBuffer.size()), m_group); + + // check map + for (auto& family : families) + { + std::vector actors(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + std::set actorsSet; + for (auto actor : actors) + EXPECT_TRUE(actorsSet.insert(actor->getIndex()).second); + EXPECT_TRUE(m_actorsPerFamily[family] == actorsSet); + } + + // unsubscribe + for (auto& family : families) + { + family->removeListener(*this); + } + + m_test->m_groupTM->process(); + m_test->m_groupTM->wait(); + + sync->release(); + } + + // listen for Split event and update actors map + virtual void receive(const TkEvent* events, uint32_t eventCount) override + { + for (size_t i = 0; i < eventCount; ++i) + { + const TkEvent& e = events[i]; + switch (e.type) + { + case (TkEvent::Split) : + { + const TkSplitEvent* splitEvent = e.getPayload(); + auto& actorsSet = m_actorsPerFamily[splitEvent->parentData.family]; + if (!isInvalidIndex(splitEvent->parentData.index)) + { + EXPECT_EQ((size_t)1, actorsSet.erase(splitEvent->parentData.index)); + } + for (size_t i = 0; i < splitEvent->numChildren; ++i) + { + TkActor* a = splitEvent->children[i]; + EXPECT_TRUE(actorsSet.insert(a->getIndex()).second); + } + break; + } + case (TkEvent::FractureCommand) : + { + break; + } + case (TkEvent::JointUpdate) : + { + FAIL(); + break; + } + default: + break; + } + } + } + +private: + std::map> m_actorsPerFamily; + std::vector& m_syncBuffer; +}; + +TEST_F(TkTestStrict, SyncTest1) +{ + this->createFramework(); + + std::vector syncBuffer; + + std::stringstream serverFinalState; + { + Server s(this, syncBuffer); + s.run(serverFinalState); + } + EXPECT_TRUE(syncBuffer.size() > 0); + + std::stringstream clientFinalState; + { + Client c(this, syncBuffer); + c.run(clientFinalState); + } + + for (auto e : syncBuffer) + { + e->release(); + } + syncBuffer.clear(); + + EXPECT_EQ(serverFinalState.str(), clientFinalState.str()); + + this->releaseFramework(); +} diff --git a/test/src/unit/TkCompositeTests.cpp b/test/src/unit/TkCompositeTests.cpp old mode 100644 new mode 100755 index 8f55612..96fcc75 --- a/test/src/unit/TkCompositeTests.cpp +++ b/test/src/unit/TkCompositeTests.cpp @@ -1,775 +1,775 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "TkBaseTest.h" - -#include -#include -#include - -#include "PsMemoryBuffer.h" - -#include "NvBlastTime.h" - - -/* -Composite and joint tests: - -0) Test serialization of composites and assemblies - -1) Create assembly, actors and joints should be created automatically - -2) Create an actor with internal joints. Splitting the actor should cause joint create events to be dispatched - -3) Joint update events should be fired when attached actors change - -4) Joint delete events should be fired when at least one attached actor is deleted - -5) Creating a composite from assets with internal joints should have expected behaviors (1-4) above -*/ - - -struct Composite -{ - std::vector m_actorDescs; - std::vector m_relTMs; - std::vector m_jointDescs; -}; - - -template -class TkCompositeTest : public TkBaseTest -{ -public: - - // Composite/joint tests - void createAssembly(std::vector& actors, std::vector& joints, bool createNRFJoints) - { - TkFramework* fw = NvBlastTkFrameworkGet(); - - actors.resize(4, nullptr); - actors[0] = fw->createActor(TkActorDesc(testAssets[0])); - actors[1] = fw->createActor(TkActorDesc(testAssets[0])); - actors[2] = fw->createActor(TkActorDesc(testAssets[1])); - actors[3] = fw->createActor(TkActorDesc(testAssets[1])); - - std::vector families(4); - families[0] = &actors[0]->getFamily(); - families[1] = &actors[1]->getFamily(); - families[2] = &actors[2]->getFamily(); - families[3] = &actors[3]->getFamily(); - - EXPECT_FALSE(actors[0] == nullptr); - EXPECT_FALSE(actors[1] == nullptr); - EXPECT_FALSE(actors[2] == nullptr); - EXPECT_FALSE(actors[3] == nullptr); - - const TkJointDesc jointDescsNoNRF[8] = - { - // Actor indices, chunk indices, attach position in the composite frame - { { families[0], families[1] }, { 6, 5 }, { PxVec3(0.0f, -1.5f, 0.5f), PxVec3(0.0f, -1.5f, 0.5f) } }, - { { families[0], families[1] }, { 4, 3 }, { PxVec3(0.0f, -0.5f, -0.5f), PxVec3(0.0f, -0.5f, -0.5f) } }, - - { { families[0], families[2] }, { 8, 6 }, { PxVec3(-0.5f, 0.0f, 0.5f), PxVec3(-0.5f, 0.0f, 0.5f) } }, - { { families[0], families[2] }, { 3, 1 }, { PxVec3(-1.5f, 0.0f, -0.5f), PxVec3(-1.5f, 0.0f, -0.5f) } }, - - { { families[1], families[3] }, { 7, 5 }, { PxVec3(0.5f, 0.0f, 0.5f), PxVec3(0.5f, 0.0f, 0.5f) } }, - { { families[1], families[3] }, { 4, 2 }, { PxVec3(1.0f, 0.0f, -0.5f), PxVec3(1.0f, 0.0f, -0.5f) } }, - - { { families[2], families[3] }, { 8, 7 }, { PxVec3(0.0f, 1.5f, 0.5f), PxVec3(0.0f, 1.5f, 0.5f) } }, - { { families[2], families[3] }, { 2, 1 }, { PxVec3(0.0f, 0.5f, -0.5f), PxVec3(0.0f, 0.5f, -0.5f) } } - }; - - const TkJointDesc jointDescsWithNRF[12] = - { - // Actor indices, chunk indices, attach position in the composite frame - { { families[0], families[1] }, { 6, 5 }, { PxVec3(0.0f, -1.5f, 0.5f), PxVec3(0.0f, -1.5f, 0.5f) } }, - { { families[0], families[1] }, { 4, 3 }, { PxVec3(0.0f, -0.5f, -0.5f), PxVec3(0.0f, -0.5f, -0.5f) } }, - - { { families[0], nullptr }, { 8, 0xFFFFFFFF }, { PxVec3(-0.5f, 0.0f, 0.5f), PxVec3(-0.5f, 0.0f, 0.5f) } }, - { { families[0], nullptr }, { 3, 0xFFFFFFFF }, { PxVec3(-1.5f, 0.0f, -0.5f), PxVec3(-1.5f, 0.0f, -0.5f) } }, - - { { nullptr, families[2] }, { 0xFFFFFFFF, 6 }, { PxVec3(-0.5f, 0.0f, 0.5f), PxVec3(-0.5f, 0.0f, 0.5f) } }, - { { nullptr, families[2] }, { 0xFFFFFFFF, 1 }, { PxVec3(-1.5f, 0.0f, -0.5f), PxVec3(-1.5f, 0.0f, -0.5f) } }, - - { { families[1], nullptr }, { 7, 0xFFFFFFFF }, { PxVec3(0.5f, 0.0f, 0.5f), PxVec3(0.5f, 0.0f, 0.5f) } }, - { { families[1], nullptr }, { 4, 0xFFFFFFFF }, { PxVec3(1.0f, 0.0f, -0.5f), PxVec3(1.0f, 0.0f, -0.5f) } }, - - { { nullptr, families[3] }, { 0xFFFFFFFF, 5 }, { PxVec3(0.5f, 0.0f, 0.5f), PxVec3(0.5f, 0.0f, 0.5f) } }, - { { nullptr, families[3] }, { 0xFFFFFFFF, 2 }, { PxVec3(1.0f, 0.0f, -0.5f), PxVec3(1.0f, 0.0f, -0.5f) } }, - - { { families[2], families[3] }, { 8, 7 }, { PxVec3(0.0f, 1.5f, 0.5f), PxVec3(0.0f, 1.5f, 0.5f) } }, - { { families[2], families[3] }, { 2, 1 }, { PxVec3(0.0f, 0.5f, -0.5f), PxVec3(0.0f, 0.5f, -0.5f), } } - }; - - const TkJointDesc* jointDescs = createNRFJoints ? jointDescsWithNRF : jointDescsNoNRF; - const int jointCount = createNRFJoints ? 12 : 8; - - joints.resize(jointCount, nullptr); - for (int i = 0; i < jointCount; ++i) - { - joints[i] = fw->createJoint(jointDescs[i]); - EXPECT_FALSE(joints[i] == nullptr); - } - } - - void familySerialization(std::vector& families, TestFamilyTracker& tracker) - { -#if 1 - NV_UNUSED(families); - NV_UNUSED(tracker); -#else - TkFramework* fw = NvBlastTkFrameworkGet(); - - PsMemoryBuffer* membuf = PX_NEW(PsMemoryBuffer); - EXPECT_TRUE(membuf != nullptr); - if (membuf == nullptr) - { - return; - } - - std::vector oldFamilies = families; - - for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) - { - GTEST_FATAL_FAILURE_("Serialization of families needs to be put into extensions."); -// families[familyNum]->serialize(*membuf); - } - - for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) - { - TkFamily* f = families[familyNum]; - - std::vector actors(f->getActorCount()); - f->getActors(actors.data(), static_cast(actors.size())); - for (auto a : actors) - { - tracker.eraseActor(a); - } - - f->release(); - families[familyNum] = nullptr; - } - - for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) - { - GTEST_FATAL_FAILURE_("Deserialization of families needs to be put into extensions."); -// TkFamily* f = reinterpret_cast(fw->deserialize(*membuf)); -// f->addListener(tracker); -// families[familyNum] = f; - } - - for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) - { - TkFamily* f = families[familyNum]; - - std::vector actors(f->getActorCount()); - f->getActors(actors.data(), static_cast(actors.size())); - for (auto a : actors) - { - tracker.insertActor(a); - - std::vector joints(a->getJointCount()); - a->getJoints(joints.data(), (uint32_t)joints.size()); - - for (auto j : joints) - { - const TkJointData jd = j->getData(); - if (jd.actors[0] != jd.actors[1]) - { - tracker.joints.insert(j); - } - } - } - } - - membuf->release(); -#endif - } - - void recollectActors(std::vector& families, std::vector& actors) - { - uint32_t totalActorCount = 0; - for (auto family : families) - { - EXPECT_LE(family->getActorCount() + totalActorCount, actors.size()); - totalActorCount += family->getActors(actors.data() + totalActorCount, static_cast(actors.size()) - totalActorCount); - } - } - - void assemblyCreateAndRelease(bool createNRFJoints, bool serializationTest) - { - createFramework(); - createTestAssets(); - - TkFramework* fw = NvBlastTkFrameworkGet(); - - const TkType* familyType = fw->getType(TkTypeIndex::Family); - EXPECT_TRUE(familyType != nullptr); - - TestFamilyTracker tracker; - - std::vector families1; - std::vector families2; - - // Create one assembly - std::vector actors1; - std::vector joints1; - createAssembly(actors1, joints1, createNRFJoints); - tracker.joints.insert(joints1.begin(), joints1.end()); - - // Create another assembly - std::vector actors2; - std::vector joints2; - createAssembly(actors2, joints2, createNRFJoints); - tracker.joints.insert(joints2.begin(), joints2.end()); - - // Store families and fill group - for (size_t actorNum = 0; actorNum < actors1.size(); ++actorNum) - { - TkFamily& family = actors1[actorNum]->getFamily(); - families1.push_back(&family); - family.addListener(tracker); - } - for (size_t actorNum = 0; actorNum < actors2.size(); ++actorNum) - { - TkFamily& family = actors2[actorNum]->getFamily(); - families2.push_back(&family); - family.addListener(tracker); - } - - if (serializationTest) - { - familySerialization(families1, tracker); - recollectActors(families1, actors1); - familySerialization(families2, tracker); - recollectActors(families2, actors2); - } - - EXPECT_EQ(joints1.size() + joints2.size(), tracker.joints.size()); - - // Release 1st assembly's actors - for (size_t actorNum = 0; actorNum < actors1.size(); ++actorNum) - { - actors1[actorNum]->release(); - } - - if (serializationTest) - { - familySerialization(families2, tracker); - recollectActors(families2, actors2); - } - - EXPECT_EQ(joints2.size(), tracker.joints.size()); - - // Release 2nd assembly's actors - for (size_t actorNum = 0; actorNum < actors1.size(); ++actorNum) - { - actors2[actorNum]->release(); - } - - EXPECT_EQ(0, tracker.joints.size()); - - releaseTestAssets(); - releaseFramework(); - } - - void assemblyInternalJoints(bool testAssemblySerialization) - { - createFramework(); - createTestAssets(true); // Create assets with internal joints - - TkFramework* fw = NvBlastTkFrameworkGet(); - - TestFamilyTracker tracker; - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fw->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - TkActorDesc adesc(testAssets[0]); - - TkActor* actor1 = fw->createActor(adesc); - EXPECT_TRUE(actor1 != nullptr); - tracker.insertActor(actor1); - - actor1->getFamily().addListener(tracker); - - TkFamily* family = &actor1->getFamily(); - - group->addActor(*actor1); - - CSParams cs2(2, 0.0f); - NvBlastExtProgramParams csParams2 = { &cs2, nullptr }; - actor1->damage(getCubeSlicerProgram(), &csParams2); - - EXPECT_EQ((size_t)0, tracker.joints.size()); - - m_groupTM->process(); - m_groupTM->wait(); - - if (testAssemblySerialization) - { - std::vector families; - families.push_back(family); - familySerialization(families, tracker); - family = families[0]; - std::vector actors(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - for (TkActor* actor : actors) - { - group->addActor(*actor); - } - } - - EXPECT_EQ((size_t)2, family->getActorCount()); - EXPECT_EQ((size_t)4, tracker.joints.size()); // 2) Create an actor with internal joints. Splitting the actor should cause joint create events to be dispatched - - std::vector actors(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - - for (TkJoint* joint : tracker.joints) - { - TkJointData jd = joint->getData(); - EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[0])); - EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[1])); - } - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialParams = { &radialDamage, nullptr }; - for (TkActor* actor : actors) - { - actor->damage(getFalloffProgram(), &radialParams); - } - - m_groupTM->process(); - m_groupTM->wait(); - - if (testAssemblySerialization) - { - std::vector families; - families.push_back(family); - familySerialization(families, tracker); - family = families[0]; - } - - EXPECT_EQ((size_t)8, family->getActorCount()); - EXPECT_EQ((size_t)4, tracker.joints.size()); - - // 3) Joint update events should be fired when attached actors change - - actors.resize(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - - for (TkJoint* joint : tracker.joints) - { - TkJointData jd = joint->getData(); - EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[0])); - EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[1])); - } - - for (TkActor* actor : actors) - { - actor->release(); - } - - EXPECT_EQ((size_t)0, tracker.joints.size()); // 4) Joint delete events should be fired when at least one attached actor is deleted - - group->release(); - - releaseTestAssets(); - releaseFramework(); - } - - void assemblyCompositeWithInternalJoints(bool createNRFJoints, bool serializationTest) - { - createFramework(); - createTestAssets(true); // Create assets with internal joints - - TkFramework* fw = NvBlastTkFrameworkGet(); - - const TkType* familyType = fw->getType(TkTypeIndex::Family); - EXPECT_TRUE(familyType != nullptr); - - if (familyType == nullptr) - { - return; - } - - TestFamilyTracker tracker; - - std::vector families; - - // Create assembly - std::vector actors; - std::vector joints; - createAssembly(actors, joints, createNRFJoints); - tracker.joints.insert(joints.begin(), joints.end()); - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fw->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - for (size_t i = 0; i < actors.size(); ++i) - { - TkFamily& family = actors[i]->getFamily(); - families.push_back(&family); - family.addListener(tracker); - tracker.insertActor(actors[i]); - group->addActor(*actors[i]); - } - - if (serializationTest) - { - familySerialization(families, tracker); - recollectActors(families, actors); - for (auto actor : actors) - { - group->addActor(*actor); - } - } - - EXPECT_EQ((size_t)4, actors.size()); - - const size_t compJointCount = createNRFJoints ? (size_t)12 : (size_t)8; - - EXPECT_EQ(compJointCount, tracker.joints.size()); - - CSParams cs2(2, 0.0f); - NvBlastExtProgramParams csParams2 = { &cs2, nullptr }; - - size_t totalActorCount = 0; - for (uint32_t i = 0; i < 4; ++i) - { - actors[i]->damage(getCubeSlicerProgram(), &csParams2); - - m_groupTM->process(); - m_groupTM->wait(); - - if (serializationTest) - { - familySerialization(families, tracker); - for (size_t j = 0; j < families.size(); ++j) - { - TkFamily* family = families[j]; - std::vector a(family->getActorCount()); - family->getActors(a.data(), static_cast(a.size())); - for (auto actor : a) - { - group->addActor(*actor); - } - EXPECT_TRUE(j <= i || a.size() == 1); - if (j > i && a.size() == 1) - { - actors[j] = a[0]; - } - } - } - - EXPECT_EQ((size_t)2, families[i]->getActorCount()); - EXPECT_EQ((size_t)(compJointCount + 4 * (i + 1)), tracker.joints.size()); // Four joints created per actor - - totalActorCount += families[i]->getActorCount(); - } - - actors.resize(totalActorCount); - totalActorCount = 0; - for (int i = 0; i < 4; ++i) - { - families[i]->getActors(actors.data() + totalActorCount, families[i]->getActorCount()); - totalActorCount += families[i]->getActorCount(); - } - - for (TkJoint* joint : tracker.joints) - { - TkJointData jd = joint->getData(); - EXPECT_TRUE(jd.actors[0] == nullptr || actors.end() != std::find(actors.begin(), actors.end(), jd.actors[0])); - EXPECT_TRUE(jd.actors[1] == nullptr || actors.end() != std::find(actors.begin(), actors.end(), jd.actors[1])); - } - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialParams = { &radialDamage, nullptr }; - for (TkActor* actor : actors) - { - actor->damage(getFalloffProgram(), &radialParams); - } - - m_groupTM->process(); - m_groupTM->wait(); - - totalActorCount = 0; - for (int i = 0; i < 4; ++i) - { - totalActorCount += families[i]->getActorCount(); - } - - if (serializationTest) - { - familySerialization(families, tracker); - } - - EXPECT_EQ((size_t)32, totalActorCount); - EXPECT_EQ(compJointCount + (size_t)16, tracker.joints.size()); - - actors.resize(totalActorCount); - totalActorCount = 0; - for (int i = 0; i < 4; ++i) - { - families[i]->getActors(actors.data() + totalActorCount, families[i]->getActorCount()); - totalActorCount += families[i]->getActorCount(); - } - - // 3) Joint update events should be fired when attached actors change - - for (TkActor* actor : actors) - { - actor->release(); - } - - EXPECT_EQ((size_t)0, tracker.joints.size()); // 4) Joint delete events should be fired when at least one attached actor is deleted - - group->release(); - - releaseTestAssets(); - releaseFramework(); - } - - void assemblyExternalJoints_MultiFamilyDamage(bool explicitJointRelease = true) - { - createFramework(); - - const NvBlastChunkDesc chunkDescs[3] = - { -// centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 4.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { { 0.0f,-1.0f, 0.0f }, 2.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.0f, 1.0f, 0.0f }, 2.0f, 0, NvBlastChunkDesc::SupportFlag, 2 } - }; - - const NvBlastBondDesc bondDesc = -// normal area centroid userData chunks - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }; - - TkFramework* framework = NvBlastTkFrameworkGet(); - - TestFamilyTracker tracker; - - TkAssetDesc desc; - desc.chunkCount = 3; - desc.chunkDescs = chunkDescs; - desc.bondCount = 1; - desc.bondDescs = &bondDesc; - desc.bondFlags = nullptr; - TkAsset* asset = framework->createAsset(desc); - EXPECT_TRUE(asset != nullptr); - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = framework->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - TkActorDesc adesc(asset); - TkActor* actor1 = framework->createActor(adesc); - EXPECT_TRUE(actor1 != nullptr); - TkActor* actor2 = framework->createActor(adesc); - EXPECT_TRUE(actor2 != nullptr); - - group->addActor(*actor1); - group->addActor(*actor2); - - TkFamily* family1 = &actor1->getFamily(); - TkFamily* family2 = &actor2->getFamily(); - - family1->addListener(tracker); - family2->addListener(tracker); - tracker.insertActor(actor1); - tracker.insertActor(actor2); - - TkJointDesc jdesc; - jdesc.families[0] = family1; - jdesc.families[1] = family2; - jdesc.chunkIndices[0] = 2; - jdesc.chunkIndices[1] = 1; - jdesc.attachPositions[0] = PxVec3(0.0f, 1.0f, 0.0f); - jdesc.attachPositions[1] = PxVec3(0.0f, -1.0f, 0.0f); - TkJoint* joint = framework->createJoint(jdesc); - EXPECT_TRUE(joint != nullptr); - tracker.joints.insert(joint); - - NvBlastExtRadialDamageDesc radialDamage1 = getRadialDamageDesc(0, 1, 0, 2, 2); - NvBlastExtProgramParams radialParams1 = { &radialDamage1, nullptr }; - actor1->damage(getFalloffProgram(), &radialParams1); - NvBlastExtRadialDamageDesc radialDamage2 = getRadialDamageDesc(0, -1, 0, 2, 2); - NvBlastExtProgramParams radialParams2 = { &radialDamage2, nullptr }; - actor2->damage(getFalloffProgram(), &radialParams2); - - m_groupTM->process(); - m_groupTM->wait(); - - TkActor* actors1[2]; - TkActor* actors2[2]; - EXPECT_EQ(2, family1->getActors(actors1, 2)); - EXPECT_EQ(2, family2->getActors(actors2, 2)); - - const TkJointData jdata = joint->getData(); - EXPECT_TRUE(jdata.actors[0] != nullptr); - EXPECT_TRUE(jdata.actors[1] != nullptr); - EXPECT_TRUE(&jdata.actors[0]->getFamily() == family1); - EXPECT_TRUE(&jdata.actors[1]->getFamily() == family2); - - // Clean up - if (explicitJointRelease) - { - joint->release(); - family2->release(); - family1->release(); - asset->release(); - releaseFramework(); - } - else - { - EXPECT_EQ(1, tracker.joints.size()); - releaseFramework(); - // Commenting these out - but shouldn't we be sending delete events when we release the framework? -// EXPECT_EQ(0, tracker.joints.size()); -// EXPECT_EQ(0, tracker.actors.size()); - } - } - -protected: - // http://clang.llvm.org/compatibility.html#dep_lookup_bases - // http://stackoverflow.com/questions/6592512/templates-parent-class-member-variables-not-visible-in-inherited-class - - using TkBaseTest::testAssets; - using TkBaseTest::m_taskman; - using TkBaseTest::m_groupTM; - using TkBaseTest::createFramework; - using TkBaseTest::releaseFramework; - using TkBaseTest::createTestAssets; - using TkBaseTest::releaseTestAssets; - using TkBaseTest::getCubeSlicerProgram; - using TkBaseTest::getDefaultMaterial; - using TkBaseTest::getRadialDamageDesc; - using TkBaseTest::getFalloffProgram; -}; - - -typedef TkCompositeTest TkCompositeTestAllowWarnings; -typedef TkCompositeTest TkCompositeTestStrict; - - -/* -1) Create assembly, actors and joints should be created automatically -*/ - -TEST_F(TkCompositeTestStrict, AssemblyCreateAndRelease_NoNRFJoints_NoSerialization) -{ - assemblyCreateAndRelease(false, false); -} - -TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCreateAndRelease_NoNRFJoints_AssemblySerialization) -{ - assemblyCreateAndRelease(false, true); -} - -TEST_F(TkCompositeTestStrict, AssemblyCreateAndRelease_WithNRFJoints_NoSerialization) -{ - assemblyCreateAndRelease(true, false); -} - -TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCreateAndRelease_WithNRFJoints_AssemblySerialization) -{ - assemblyCreateAndRelease(true, true); -} - - -/** -2) Create an actor with internal joints. Splitting the actor should cause joint create events to be dispatched - -3) Joint update events should be fired when attached actors change - -4) Joint delete events should be fired when at least one attached actor is deleted -*/ - -TEST_F(TkCompositeTestStrict, AssemblyInternalJoints_NoSerialization) -{ - assemblyInternalJoints(false); -} - -TEST_F(TkCompositeTestStrict, DISABLED_AssemblyInternalJoints_AssemblySerialization) -{ - assemblyInternalJoints(true); -} - - -/** -5) Creating a composite from assets with internal joints should have expected behaviors (1-4) above -*/ - -TEST_F(TkCompositeTestStrict, AssemblyCompositeWithInternalJoints_NoNRFJoints_NoSerialization) -{ - assemblyCompositeWithInternalJoints(false, false); -} - -TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCompositeWithInternalJoints_NoNRFJoints_AssemblySerialization) -{ - assemblyCompositeWithInternalJoints(false, true); -} - -TEST_F(TkCompositeTestStrict, AssemblyCompositeWithInternalJoints_WithNRFJoints_NoSerialization) -{ - assemblyCompositeWithInternalJoints(true, false); -} - -TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCompositeWithInternalJoints_WithNRFJoints_AssemblySerialization) -{ - assemblyCompositeWithInternalJoints(true, true); -} - - -/* -More tests -*/ - -TEST_F(TkCompositeTestStrict, AssemblyExternalJoints_MultiFamilyDamage) -{ - assemblyExternalJoints_MultiFamilyDamage(true); -} - -TEST_F(TkCompositeTestStrict, AssemblyExternalJoints_MultiFamilyDamage_AutoJointRelease) -{ - assemblyExternalJoints_MultiFamilyDamage(false); -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "TkBaseTest.h" + +#include +#include +#include + +#include "PsMemoryBuffer.h" + +#include "NvBlastTime.h" + + +/* +Composite and joint tests: + +0) Test serialization of composites and assemblies + +1) Create assembly, actors and joints should be created automatically + +2) Create an actor with internal joints. Splitting the actor should cause joint create events to be dispatched + +3) Joint update events should be fired when attached actors change + +4) Joint delete events should be fired when at least one attached actor is deleted + +5) Creating a composite from assets with internal joints should have expected behaviors (1-4) above +*/ + + +struct Composite +{ + std::vector m_actorDescs; + std::vector m_relTMs; + std::vector m_jointDescs; +}; + + +template +class TkCompositeTest : public TkBaseTest +{ +public: + + // Composite/joint tests + void createAssembly(std::vector& actors, std::vector& joints, bool createNRFJoints) + { + TkFramework* fw = NvBlastTkFrameworkGet(); + + actors.resize(4, nullptr); + actors[0] = fw->createActor(TkActorDesc(testAssets[0])); + actors[1] = fw->createActor(TkActorDesc(testAssets[0])); + actors[2] = fw->createActor(TkActorDesc(testAssets[1])); + actors[3] = fw->createActor(TkActorDesc(testAssets[1])); + + std::vector families(4); + families[0] = &actors[0]->getFamily(); + families[1] = &actors[1]->getFamily(); + families[2] = &actors[2]->getFamily(); + families[3] = &actors[3]->getFamily(); + + EXPECT_FALSE(actors[0] == nullptr); + EXPECT_FALSE(actors[1] == nullptr); + EXPECT_FALSE(actors[2] == nullptr); + EXPECT_FALSE(actors[3] == nullptr); + + const TkJointDesc jointDescsNoNRF[8] = + { + // Actor indices, chunk indices, attach position in the composite frame + { { families[0], families[1] }, { 6, 5 }, { PxVec3(0.0f, -1.5f, 0.5f), PxVec3(0.0f, -1.5f, 0.5f) } }, + { { families[0], families[1] }, { 4, 3 }, { PxVec3(0.0f, -0.5f, -0.5f), PxVec3(0.0f, -0.5f, -0.5f) } }, + + { { families[0], families[2] }, { 8, 6 }, { PxVec3(-0.5f, 0.0f, 0.5f), PxVec3(-0.5f, 0.0f, 0.5f) } }, + { { families[0], families[2] }, { 3, 1 }, { PxVec3(-1.5f, 0.0f, -0.5f), PxVec3(-1.5f, 0.0f, -0.5f) } }, + + { { families[1], families[3] }, { 7, 5 }, { PxVec3(0.5f, 0.0f, 0.5f), PxVec3(0.5f, 0.0f, 0.5f) } }, + { { families[1], families[3] }, { 4, 2 }, { PxVec3(1.0f, 0.0f, -0.5f), PxVec3(1.0f, 0.0f, -0.5f) } }, + + { { families[2], families[3] }, { 8, 7 }, { PxVec3(0.0f, 1.5f, 0.5f), PxVec3(0.0f, 1.5f, 0.5f) } }, + { { families[2], families[3] }, { 2, 1 }, { PxVec3(0.0f, 0.5f, -0.5f), PxVec3(0.0f, 0.5f, -0.5f) } } + }; + + const TkJointDesc jointDescsWithNRF[12] = + { + // Actor indices, chunk indices, attach position in the composite frame + { { families[0], families[1] }, { 6, 5 }, { PxVec3(0.0f, -1.5f, 0.5f), PxVec3(0.0f, -1.5f, 0.5f) } }, + { { families[0], families[1] }, { 4, 3 }, { PxVec3(0.0f, -0.5f, -0.5f), PxVec3(0.0f, -0.5f, -0.5f) } }, + + { { families[0], nullptr }, { 8, 0xFFFFFFFF }, { PxVec3(-0.5f, 0.0f, 0.5f), PxVec3(-0.5f, 0.0f, 0.5f) } }, + { { families[0], nullptr }, { 3, 0xFFFFFFFF }, { PxVec3(-1.5f, 0.0f, -0.5f), PxVec3(-1.5f, 0.0f, -0.5f) } }, + + { { nullptr, families[2] }, { 0xFFFFFFFF, 6 }, { PxVec3(-0.5f, 0.0f, 0.5f), PxVec3(-0.5f, 0.0f, 0.5f) } }, + { { nullptr, families[2] }, { 0xFFFFFFFF, 1 }, { PxVec3(-1.5f, 0.0f, -0.5f), PxVec3(-1.5f, 0.0f, -0.5f) } }, + + { { families[1], nullptr }, { 7, 0xFFFFFFFF }, { PxVec3(0.5f, 0.0f, 0.5f), PxVec3(0.5f, 0.0f, 0.5f) } }, + { { families[1], nullptr }, { 4, 0xFFFFFFFF }, { PxVec3(1.0f, 0.0f, -0.5f), PxVec3(1.0f, 0.0f, -0.5f) } }, + + { { nullptr, families[3] }, { 0xFFFFFFFF, 5 }, { PxVec3(0.5f, 0.0f, 0.5f), PxVec3(0.5f, 0.0f, 0.5f) } }, + { { nullptr, families[3] }, { 0xFFFFFFFF, 2 }, { PxVec3(1.0f, 0.0f, -0.5f), PxVec3(1.0f, 0.0f, -0.5f) } }, + + { { families[2], families[3] }, { 8, 7 }, { PxVec3(0.0f, 1.5f, 0.5f), PxVec3(0.0f, 1.5f, 0.5f) } }, + { { families[2], families[3] }, { 2, 1 }, { PxVec3(0.0f, 0.5f, -0.5f), PxVec3(0.0f, 0.5f, -0.5f), } } + }; + + const TkJointDesc* jointDescs = createNRFJoints ? jointDescsWithNRF : jointDescsNoNRF; + const int jointCount = createNRFJoints ? 12 : 8; + + joints.resize(jointCount, nullptr); + for (int i = 0; i < jointCount; ++i) + { + joints[i] = fw->createJoint(jointDescs[i]); + EXPECT_FALSE(joints[i] == nullptr); + } + } + + void familySerialization(std::vector& families, TestFamilyTracker& tracker) + { +#if 1 + NV_UNUSED(families); + NV_UNUSED(tracker); +#else + TkFramework* fw = NvBlastTkFrameworkGet(); + + PsMemoryBuffer* membuf = PX_NEW(PsMemoryBuffer); + EXPECT_TRUE(membuf != nullptr); + if (membuf == nullptr) + { + return; + } + + std::vector oldFamilies = families; + + for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) + { + GTEST_FATAL_FAILURE_("Serialization of families needs to be put into extensions."); +// families[familyNum]->serialize(*membuf); + } + + for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) + { + TkFamily* f = families[familyNum]; + + std::vector actors(f->getActorCount()); + f->getActors(actors.data(), static_cast(actors.size())); + for (auto a : actors) + { + tracker.eraseActor(a); + } + + f->release(); + families[familyNum] = nullptr; + } + + for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) + { + GTEST_FATAL_FAILURE_("Deserialization of families needs to be put into extensions."); +// TkFamily* f = reinterpret_cast(fw->deserialize(*membuf)); +// f->addListener(tracker); +// families[familyNum] = f; + } + + for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) + { + TkFamily* f = families[familyNum]; + + std::vector actors(f->getActorCount()); + f->getActors(actors.data(), static_cast(actors.size())); + for (auto a : actors) + { + tracker.insertActor(a); + + std::vector joints(a->getJointCount()); + a->getJoints(joints.data(), (uint32_t)joints.size()); + + for (auto j : joints) + { + const TkJointData jd = j->getData(); + if (jd.actors[0] != jd.actors[1]) + { + tracker.joints.insert(j); + } + } + } + } + + membuf->release(); +#endif + } + + void recollectActors(std::vector& families, std::vector& actors) + { + uint32_t totalActorCount = 0; + for (auto family : families) + { + EXPECT_LE(family->getActorCount() + totalActorCount, actors.size()); + totalActorCount += family->getActors(actors.data() + totalActorCount, static_cast(actors.size()) - totalActorCount); + } + } + + void assemblyCreateAndRelease(bool createNRFJoints, bool serializationTest) + { + createFramework(); + createTestAssets(); + + TkFramework* fw = NvBlastTkFrameworkGet(); + + const TkType* familyType = fw->getType(TkTypeIndex::Family); + EXPECT_TRUE(familyType != nullptr); + + TestFamilyTracker tracker; + + std::vector families1; + std::vector families2; + + // Create one assembly + std::vector actors1; + std::vector joints1; + createAssembly(actors1, joints1, createNRFJoints); + tracker.joints.insert(joints1.begin(), joints1.end()); + + // Create another assembly + std::vector actors2; + std::vector joints2; + createAssembly(actors2, joints2, createNRFJoints); + tracker.joints.insert(joints2.begin(), joints2.end()); + + // Store families and fill group + for (size_t actorNum = 0; actorNum < actors1.size(); ++actorNum) + { + TkFamily& family = actors1[actorNum]->getFamily(); + families1.push_back(&family); + family.addListener(tracker); + } + for (size_t actorNum = 0; actorNum < actors2.size(); ++actorNum) + { + TkFamily& family = actors2[actorNum]->getFamily(); + families2.push_back(&family); + family.addListener(tracker); + } + + if (serializationTest) + { + familySerialization(families1, tracker); + recollectActors(families1, actors1); + familySerialization(families2, tracker); + recollectActors(families2, actors2); + } + + EXPECT_EQ(joints1.size() + joints2.size(), tracker.joints.size()); + + // Release 1st assembly's actors + for (size_t actorNum = 0; actorNum < actors1.size(); ++actorNum) + { + actors1[actorNum]->release(); + } + + if (serializationTest) + { + familySerialization(families2, tracker); + recollectActors(families2, actors2); + } + + EXPECT_EQ(joints2.size(), tracker.joints.size()); + + // Release 2nd assembly's actors + for (size_t actorNum = 0; actorNum < actors1.size(); ++actorNum) + { + actors2[actorNum]->release(); + } + + EXPECT_EQ(0, tracker.joints.size()); + + releaseTestAssets(); + releaseFramework(); + } + + void assemblyInternalJoints(bool testAssemblySerialization) + { + createFramework(); + createTestAssets(true); // Create assets with internal joints + + TkFramework* fw = NvBlastTkFrameworkGet(); + + TestFamilyTracker tracker; + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fw->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + TkActorDesc adesc(testAssets[0]); + + TkActor* actor1 = fw->createActor(adesc); + EXPECT_TRUE(actor1 != nullptr); + tracker.insertActor(actor1); + + actor1->getFamily().addListener(tracker); + + TkFamily* family = &actor1->getFamily(); + + group->addActor(*actor1); + + CSParams cs2(2, 0.0f); + NvBlastExtProgramParams csParams2 = { &cs2, nullptr }; + actor1->damage(getCubeSlicerProgram(), &csParams2); + + EXPECT_EQ((size_t)0, tracker.joints.size()); + + m_groupTM->process(); + m_groupTM->wait(); + + if (testAssemblySerialization) + { + std::vector families; + families.push_back(family); + familySerialization(families, tracker); + family = families[0]; + std::vector actors(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + for (TkActor* actor : actors) + { + group->addActor(*actor); + } + } + + EXPECT_EQ((size_t)2, family->getActorCount()); + EXPECT_EQ((size_t)4, tracker.joints.size()); // 2) Create an actor with internal joints. Splitting the actor should cause joint create events to be dispatched + + std::vector actors(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + + for (TkJoint* joint : tracker.joints) + { + TkJointData jd = joint->getData(); + EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[0])); + EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[1])); + } + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialParams = { &radialDamage, nullptr }; + for (TkActor* actor : actors) + { + actor->damage(getFalloffProgram(), &radialParams); + } + + m_groupTM->process(); + m_groupTM->wait(); + + if (testAssemblySerialization) + { + std::vector families; + families.push_back(family); + familySerialization(families, tracker); + family = families[0]; + } + + EXPECT_EQ((size_t)8, family->getActorCount()); + EXPECT_EQ((size_t)4, tracker.joints.size()); + + // 3) Joint update events should be fired when attached actors change + + actors.resize(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + + for (TkJoint* joint : tracker.joints) + { + TkJointData jd = joint->getData(); + EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[0])); + EXPECT_FALSE(actors.end() == std::find(actors.begin(), actors.end(), jd.actors[1])); + } + + for (TkActor* actor : actors) + { + actor->release(); + } + + EXPECT_EQ((size_t)0, tracker.joints.size()); // 4) Joint delete events should be fired when at least one attached actor is deleted + + group->release(); + + releaseTestAssets(); + releaseFramework(); + } + + void assemblyCompositeWithInternalJoints(bool createNRFJoints, bool serializationTest) + { + createFramework(); + createTestAssets(true); // Create assets with internal joints + + TkFramework* fw = NvBlastTkFrameworkGet(); + + const TkType* familyType = fw->getType(TkTypeIndex::Family); + EXPECT_TRUE(familyType != nullptr); + + if (familyType == nullptr) + { + return; + } + + TestFamilyTracker tracker; + + std::vector families; + + // Create assembly + std::vector actors; + std::vector joints; + createAssembly(actors, joints, createNRFJoints); + tracker.joints.insert(joints.begin(), joints.end()); + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fw->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + for (size_t i = 0; i < actors.size(); ++i) + { + TkFamily& family = actors[i]->getFamily(); + families.push_back(&family); + family.addListener(tracker); + tracker.insertActor(actors[i]); + group->addActor(*actors[i]); + } + + if (serializationTest) + { + familySerialization(families, tracker); + recollectActors(families, actors); + for (auto actor : actors) + { + group->addActor(*actor); + } + } + + EXPECT_EQ((size_t)4, actors.size()); + + const size_t compJointCount = createNRFJoints ? (size_t)12 : (size_t)8; + + EXPECT_EQ(compJointCount, tracker.joints.size()); + + CSParams cs2(2, 0.0f); + NvBlastExtProgramParams csParams2 = { &cs2, nullptr }; + + size_t totalActorCount = 0; + for (uint32_t i = 0; i < 4; ++i) + { + actors[i]->damage(getCubeSlicerProgram(), &csParams2); + + m_groupTM->process(); + m_groupTM->wait(); + + if (serializationTest) + { + familySerialization(families, tracker); + for (size_t j = 0; j < families.size(); ++j) + { + TkFamily* family = families[j]; + std::vector a(family->getActorCount()); + family->getActors(a.data(), static_cast(a.size())); + for (auto actor : a) + { + group->addActor(*actor); + } + EXPECT_TRUE(j <= i || a.size() == 1); + if (j > i && a.size() == 1) + { + actors[j] = a[0]; + } + } + } + + EXPECT_EQ((size_t)2, families[i]->getActorCount()); + EXPECT_EQ((size_t)(compJointCount + 4 * (i + 1)), tracker.joints.size()); // Four joints created per actor + + totalActorCount += families[i]->getActorCount(); + } + + actors.resize(totalActorCount); + totalActorCount = 0; + for (int i = 0; i < 4; ++i) + { + families[i]->getActors(actors.data() + totalActorCount, families[i]->getActorCount()); + totalActorCount += families[i]->getActorCount(); + } + + for (TkJoint* joint : tracker.joints) + { + TkJointData jd = joint->getData(); + EXPECT_TRUE(jd.actors[0] == nullptr || actors.end() != std::find(actors.begin(), actors.end(), jd.actors[0])); + EXPECT_TRUE(jd.actors[1] == nullptr || actors.end() != std::find(actors.begin(), actors.end(), jd.actors[1])); + } + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialParams = { &radialDamage, nullptr }; + for (TkActor* actor : actors) + { + actor->damage(getFalloffProgram(), &radialParams); + } + + m_groupTM->process(); + m_groupTM->wait(); + + totalActorCount = 0; + for (int i = 0; i < 4; ++i) + { + totalActorCount += families[i]->getActorCount(); + } + + if (serializationTest) + { + familySerialization(families, tracker); + } + + EXPECT_EQ((size_t)32, totalActorCount); + EXPECT_EQ(compJointCount + (size_t)16, tracker.joints.size()); + + actors.resize(totalActorCount); + totalActorCount = 0; + for (int i = 0; i < 4; ++i) + { + families[i]->getActors(actors.data() + totalActorCount, families[i]->getActorCount()); + totalActorCount += families[i]->getActorCount(); + } + + // 3) Joint update events should be fired when attached actors change + + for (TkActor* actor : actors) + { + actor->release(); + } + + EXPECT_EQ((size_t)0, tracker.joints.size()); // 4) Joint delete events should be fired when at least one attached actor is deleted + + group->release(); + + releaseTestAssets(); + releaseFramework(); + } + + void assemblyExternalJoints_MultiFamilyDamage(bool explicitJointRelease = true) + { + createFramework(); + + const NvBlastChunkDesc chunkDescs[3] = + { +// centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 4.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { { 0.0f,-1.0f, 0.0f }, 2.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.0f, 1.0f, 0.0f }, 2.0f, 0, NvBlastChunkDesc::SupportFlag, 2 } + }; + + const NvBlastBondDesc bondDesc = +// normal area centroid userData chunks + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.0f, 0.0f, 0.0f }, 0 }, { 1, 2 } }; + + TkFramework* framework = NvBlastTkFrameworkGet(); + + TestFamilyTracker tracker; + + TkAssetDesc desc; + desc.chunkCount = 3; + desc.chunkDescs = chunkDescs; + desc.bondCount = 1; + desc.bondDescs = &bondDesc; + desc.bondFlags = nullptr; + TkAsset* asset = framework->createAsset(desc); + EXPECT_TRUE(asset != nullptr); + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = framework->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + TkActorDesc adesc(asset); + TkActor* actor1 = framework->createActor(adesc); + EXPECT_TRUE(actor1 != nullptr); + TkActor* actor2 = framework->createActor(adesc); + EXPECT_TRUE(actor2 != nullptr); + + group->addActor(*actor1); + group->addActor(*actor2); + + TkFamily* family1 = &actor1->getFamily(); + TkFamily* family2 = &actor2->getFamily(); + + family1->addListener(tracker); + family2->addListener(tracker); + tracker.insertActor(actor1); + tracker.insertActor(actor2); + + TkJointDesc jdesc; + jdesc.families[0] = family1; + jdesc.families[1] = family2; + jdesc.chunkIndices[0] = 2; + jdesc.chunkIndices[1] = 1; + jdesc.attachPositions[0] = PxVec3(0.0f, 1.0f, 0.0f); + jdesc.attachPositions[1] = PxVec3(0.0f, -1.0f, 0.0f); + TkJoint* joint = framework->createJoint(jdesc); + EXPECT_TRUE(joint != nullptr); + tracker.joints.insert(joint); + + NvBlastExtRadialDamageDesc radialDamage1 = getRadialDamageDesc(0, 1, 0, 2, 2); + NvBlastExtProgramParams radialParams1 = { &radialDamage1, nullptr }; + actor1->damage(getFalloffProgram(), &radialParams1); + NvBlastExtRadialDamageDesc radialDamage2 = getRadialDamageDesc(0, -1, 0, 2, 2); + NvBlastExtProgramParams radialParams2 = { &radialDamage2, nullptr }; + actor2->damage(getFalloffProgram(), &radialParams2); + + m_groupTM->process(); + m_groupTM->wait(); + + TkActor* actors1[2]; + TkActor* actors2[2]; + EXPECT_EQ(2, family1->getActors(actors1, 2)); + EXPECT_EQ(2, family2->getActors(actors2, 2)); + + const TkJointData jdata = joint->getData(); + EXPECT_TRUE(jdata.actors[0] != nullptr); + EXPECT_TRUE(jdata.actors[1] != nullptr); + EXPECT_TRUE(&jdata.actors[0]->getFamily() == family1); + EXPECT_TRUE(&jdata.actors[1]->getFamily() == family2); + + // Clean up + if (explicitJointRelease) + { + joint->release(); + family2->release(); + family1->release(); + asset->release(); + releaseFramework(); + } + else + { + EXPECT_EQ(1, tracker.joints.size()); + releaseFramework(); + // Commenting these out - but shouldn't we be sending delete events when we release the framework? +// EXPECT_EQ(0, tracker.joints.size()); +// EXPECT_EQ(0, tracker.actors.size()); + } + } + +protected: + // http://clang.llvm.org/compatibility.html#dep_lookup_bases + // http://stackoverflow.com/questions/6592512/templates-parent-class-member-variables-not-visible-in-inherited-class + + using TkBaseTest::testAssets; + using TkBaseTest::m_taskman; + using TkBaseTest::m_groupTM; + using TkBaseTest::createFramework; + using TkBaseTest::releaseFramework; + using TkBaseTest::createTestAssets; + using TkBaseTest::releaseTestAssets; + using TkBaseTest::getCubeSlicerProgram; + using TkBaseTest::getDefaultMaterial; + using TkBaseTest::getRadialDamageDesc; + using TkBaseTest::getFalloffProgram; +}; + + +typedef TkCompositeTest TkCompositeTestAllowWarnings; +typedef TkCompositeTest TkCompositeTestStrict; + + +/* +1) Create assembly, actors and joints should be created automatically +*/ + +TEST_F(TkCompositeTestStrict, AssemblyCreateAndRelease_NoNRFJoints_NoSerialization) +{ + assemblyCreateAndRelease(false, false); +} + +TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCreateAndRelease_NoNRFJoints_AssemblySerialization) +{ + assemblyCreateAndRelease(false, true); +} + +TEST_F(TkCompositeTestStrict, AssemblyCreateAndRelease_WithNRFJoints_NoSerialization) +{ + assemblyCreateAndRelease(true, false); +} + +TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCreateAndRelease_WithNRFJoints_AssemblySerialization) +{ + assemblyCreateAndRelease(true, true); +} + + +/** +2) Create an actor with internal joints. Splitting the actor should cause joint create events to be dispatched + +3) Joint update events should be fired when attached actors change + +4) Joint delete events should be fired when at least one attached actor is deleted +*/ + +TEST_F(TkCompositeTestStrict, AssemblyInternalJoints_NoSerialization) +{ + assemblyInternalJoints(false); +} + +TEST_F(TkCompositeTestStrict, DISABLED_AssemblyInternalJoints_AssemblySerialization) +{ + assemblyInternalJoints(true); +} + + +/** +5) Creating a composite from assets with internal joints should have expected behaviors (1-4) above +*/ + +TEST_F(TkCompositeTestStrict, AssemblyCompositeWithInternalJoints_NoNRFJoints_NoSerialization) +{ + assemblyCompositeWithInternalJoints(false, false); +} + +TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCompositeWithInternalJoints_NoNRFJoints_AssemblySerialization) +{ + assemblyCompositeWithInternalJoints(false, true); +} + +TEST_F(TkCompositeTestStrict, AssemblyCompositeWithInternalJoints_WithNRFJoints_NoSerialization) +{ + assemblyCompositeWithInternalJoints(true, false); +} + +TEST_F(TkCompositeTestStrict, DISABLED_AssemblyCompositeWithInternalJoints_WithNRFJoints_AssemblySerialization) +{ + assemblyCompositeWithInternalJoints(true, true); +} + + +/* +More tests +*/ + +TEST_F(TkCompositeTestStrict, AssemblyExternalJoints_MultiFamilyDamage) +{ + assemblyExternalJoints_MultiFamilyDamage(true); +} + +TEST_F(TkCompositeTestStrict, AssemblyExternalJoints_MultiFamilyDamage_AutoJointRelease) +{ + assemblyExternalJoints_MultiFamilyDamage(false); +} diff --git a/test/src/unit/TkTests.cpp b/test/src/unit/TkTests.cpp old mode 100644 new mode 100755 index e07d4a0..9687c68 --- a/test/src/unit/TkTests.cpp +++ b/test/src/unit/TkTests.cpp @@ -1,1607 +1,1607 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "TkBaseTest.h" - -#include -#include -#include - -#include "PsMemoryBuffer.h" - -#include "NvBlastTime.h" - -#include "NvBlastExtPxTask.h" - -struct ExpectedVisibleChunks -{ - ExpectedVisibleChunks() :numActors(0), numChunks(0) {} - ExpectedVisibleChunks(size_t a, size_t c) :numActors(a), numChunks(c) {} - size_t numActors; size_t numChunks; -}; - -void testResults(std::vector& families, std::map& expectedVisibleChunks) -{ - size_t numActors = 0; - for (TkFamily* fam : families) - { - auto ex = expectedVisibleChunks[fam]; - EXPECT_EQ(ex.numActors, fam->getActorCount()); - numActors += ex.numActors; - std::vector actors(fam->getActorCount()); - fam->getActors(actors.data(), static_cast(actors.size())); - for (TkActor* actor : actors) - { - EXPECT_EQ(ex.numChunks, actor->getVisibleChunkCount()); - } - } - - size_t numActorsExpected = 0; - for (auto expected : expectedVisibleChunks) - { - numActorsExpected += expected.second.numActors; - } - - EXPECT_EQ(numActorsExpected, numActors); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Tests -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -TEST_F(TkTestStrict, CreateFramework) -{ - createFramework(); - releaseFramework(); -} - -TEST_F(TkTestStrict, CreateAsset) -{ - createFramework(); - - createTestAssets(); - releaseTestAssets(); - - releaseFramework(); -} - -#if USE_PHYSX_DISPATCHER -TEST_F(TkTestStrict, DISABLED_MemLeak) -{ - PxFoundation* pxFoundation = PxCreateFoundation(PX_FOUNDATION_VERSION, NvBlastGetPxAllocatorCallback(), NvBlastGetPxErrorCallback()); - PxU32 affinity[] = { 1, 2, 4, 8 }; - PxDefaultCpuDispatcher* cpuDispatcher = PxDefaultCpuDispatcherCreate(4, affinity); - cpuDispatcher->setRunProfiled(false); - PxTaskManager* taskman = PxTaskManager::createTaskManager(NvBlastGetPxErrorCallback(), cpuDispatcher, nullptr); - - cpuDispatcher->release(); - taskman->release(); - pxFoundation->release(); -} -#endif - -TEST_F(TkTestStrict, ActorDamageNoGroup) -{ - createFramework(); - createTestAssets(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - TkActorDesc actorDesc; - actorDesc.asset = testAssets[0]; - TkActor* actor = fwk->createActor(actorDesc); - - const size_t bondFractureCount = 4; - NvBlastFractureBuffers commands; - NvBlastBondFractureData bdata[bondFractureCount]; - for (uint32_t i = 0; i < bondFractureCount; i++) - { - bdata[i].nodeIndex0 = 2 * i + 0; - bdata[i].nodeIndex1 = 2 * i + 1; - bdata[i].health = 1.0f; - } - commands.bondFractureCount = bondFractureCount; - commands.bondFractures = bdata; - commands.chunkFractureCount = 0; - commands.chunkFractures = nullptr; - actor->applyFracture(&commands, &commands); - - TkFamily& family = actor->getFamily(); - - EXPECT_TRUE(commands.bondFractureCount == 4); - EXPECT_TRUE(actor->isPending()); - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - group->addActor(*actor); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_FALSE(actor->isPending()); - EXPECT_EQ(2, family.getActorCount()); - - releaseFramework(); -} - -TEST_F(TkTestStrict, ActorDamageGroup) -{ - TEST_ZONE_BEGIN("ActorDamageGroup"); - - createFramework(); - createTestAssets(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - TestFamilyTracker ftrack1, ftrack2; - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - - NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); - NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; - - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - std::vector families; - TkFamily* trackedFamily; - std::map expectedVisibleChunks; - - { - TkActorDesc adesc(testAssets[0]); - - TkActor* actor1 = fwk->createActor(adesc); - EXPECT_TRUE(actor1 != nullptr); - - TkActor* actor2 = fwk->createActor(adesc); - EXPECT_TRUE(actor2 != nullptr); - - - expectedVisibleChunks[&actor1->getFamily()] = ExpectedVisibleChunks(8, 1); // full damage - expectedVisibleChunks[&actor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split - - GeneratorAsset cube; - TkAssetDesc assetDesc; - generateCube(cube, assetDesc, 5, 2); - assetDesc.bondFlags = nullptr; - - TkAsset* cubeAsset = fwk->createAsset(assetDesc); - testAssets.push_back(cubeAsset); - - TkActorDesc cubeAD(cubeAsset); - - TkActor* cubeActor1 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor1 != nullptr); - - trackedFamily = &cubeActor1->getFamily(); - cubeActor1->getFamily().addListener(ftrack1); - - TkActor* cubeActor2 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor2 != nullptr); - - expectedVisibleChunks[&cubeActor1->getFamily()] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each - expectedVisibleChunks[&cubeActor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split - - ftrack1.insertActor(cubeActor1); - ftrack2.insertActor(actor1); - - actor1->getFamily().addListener(ftrack2); - - TEST_ZONE_BEGIN("add to groups"); - group->addActor(*cubeActor1); - group->addActor(*cubeActor2); - group->addActor(*actor1); - group->addActor(*actor2); - TEST_ZONE_END("add to groups"); - - families.push_back(&cubeActor1->getFamily()); - families.push_back(&cubeActor2->getFamily()); - families.push_back(&actor1->getFamily()); - families.push_back(&actor2->getFamily()); - - cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); - actor1->damage(getFalloffProgram(), &radialDamageParams); - } - - EXPECT_FALSE(group->endProcess()); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - for (TkActor* actor : actors) - { - actor->damage(getCubeSlicerProgram(), &csDamageParams1); - } - } - expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4, 2); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - for (TkActor* actor : actors) - { - actor->damage(getCubeSlicerProgram(), &csDamageParams2); - } - } - - expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(8, 1); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - TEST_ZONE_BEGIN("damage"); - for (TkActor* actor : actors) - { - actor->damage(getFalloffProgram(), &radialDamageParams); - } - TEST_ZONE_END("damage"); - } - expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4096, 1); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - TEST_ZONE_BEGIN("damage"); - for (TkActor* actor : actors) - { - actor->damage(getShearProgram(), &shearDamageParams); - } - TEST_ZONE_END("damage"); - } - - m_groupTM->process(); - m_groupTM->wait(); - - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - TEST_ZONE_BEGIN("damage"); - for (TkActor* actor : actors) - { - actor->damage(getShearProgram(), &shearDamageParams); - } - TEST_ZONE_END("damage"); - } - - m_groupTM->process(); - m_groupTM->wait(); - - group->release(); - - TEST_ZONE_BEGIN("family release"); - trackedFamily->release(); - TEST_ZONE_END("family release"); - - releaseTestAssets(); - releaseFramework(); - - TEST_ZONE_END("ActorDamageGroup"); -} - - -TEST_F(TkTestStrict, ActorDamageMultiGroup) -{ - createFramework(); - createTestAssets(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - TestFamilyTracker ftrack1, ftrack2; - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group0 = fwk->createGroup(gdesc); - EXPECT_TRUE(group0 != nullptr); - TkGroup* group1 = fwk->createGroup(gdesc); - EXPECT_TRUE(group1 != nullptr); - - ExtGroupTaskManager& gtm1 = *ExtGroupTaskManager::create(*m_taskman, group1); - ExtGroupTaskManager& gtm0 = *ExtGroupTaskManager::create(*m_taskman, group0); - - std::vector families(2); - std::map expectedVisibleChunks; - - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - // prepare 2 equal actors/families and damage - { - GeneratorAsset cube; - TkAssetDesc assetDesc; - generateCube(cube, assetDesc, 6, 2, 5); - assetDesc.bondFlags = nullptr; - - TkAsset* cubeAsset = fwk->createAsset(assetDesc); - testAssets.push_back(cubeAsset); - - TkActorDesc cubeAD(cubeAsset); - - TkActor* cubeActor0 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor0 != nullptr); - cubeActor0->getFamily().addListener(ftrack1); - - TkActor* cubeActor1 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor1 != nullptr); - cubeActor1->getFamily().addListener(ftrack2); - - ftrack1.insertActor(cubeActor0); - ftrack2.insertActor(cubeActor1); - - group0->addActor(*cubeActor0); - group1->addActor(*cubeActor1); - - families[0] = (&cubeActor0->getFamily()); - families[1] = (&cubeActor1->getFamily()); - - { - cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams0); - cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams1); - - cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); - } - - expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(4, 2); // split in 4, 2 chunks each - expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each - } - - // async process 2 groups - { - EXPECT_GT(gtm0.process(2), (uint32_t)0); - EXPECT_GT(gtm1.process(2), (uint32_t)0); - uint32_t completed = 0; - while (completed < 2) - { - if (gtm0.wait(false)) - completed++; - if (gtm1.wait(false)) - completed++; - } - } - - // checks - testResults(families, expectedVisibleChunks); - EXPECT_EQ(families[0]->getActorCount(), 4); - EXPECT_EQ(group0->getActorCount(), 4); - EXPECT_EQ(families[1]->getActorCount(), 2); - EXPECT_EQ(group1->getActorCount(), 2); - - // we have group0 with 4 actors 2 chunks: - // group0: [2]' [2]' [2]' [2]' (family0') - // group1: [4]'' [4]'' (family1'') - // rearrange: - // group0: [2]' [2]' [4]'' - // group1: [4]'' [2]' [2]' - { - TkActor* group0Actors[2]; - group0->getActors(group0Actors, 2, 1); // start index: 1, because..why not? - TkActor* group1Actors[2]; - group1->getActors(group1Actors, 2, 0); - group0Actors[0]->removeFromGroup(); - group1->addActor(*group0Actors[0]); - group0Actors[1]->removeFromGroup(); - group1->addActor(*group0Actors[1]); - group1Actors[0]->removeFromGroup(); - group0->addActor(*group1Actors[0]); - } - - // checks - EXPECT_EQ(families[0]->getActorCount(), 4); - EXPECT_EQ(group0->getActorCount(), 3); - EXPECT_EQ(families[1]->getActorCount(), 2); - EXPECT_EQ(group1->getActorCount(), 3); - - // damage all - { - TkActor* allActors[6]; - families[0]->getActors(allActors, 4, 0); - families[1]->getActors(allActors + 4, 2, 0); - - typedef std::pair pair; - std::set combinations; - for (auto actor : allActors) - { - combinations.emplace(pair(actor->getGroup(), &actor->getFamily())); - if (actor->getVisibleChunkCount() == 4) - { - actor->damage(getCubeSlicerProgram(), &csDamageParams1); - } - actor->damage(getCubeSlicerProgram(), &csDamageParams2); - } - EXPECT_EQ(combinations.size(), 4); - - expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each - expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each - } - - // async process 2 groups - { - EXPECT_GT(gtm1.process(2), (uint32_t)0); - EXPECT_GT(gtm0.process(2), (uint32_t)0); - uint32_t completed = 0; - while (completed < 2) - { - if (gtm1.wait(false)) - completed++; - if (gtm0.wait(false)) - completed++; - } - } - - // checks - testResults(families, expectedVisibleChunks); - EXPECT_EQ(families[0]->getActorCount(), 8); - EXPECT_EQ(ftrack1.actors.size(), 8); - EXPECT_EQ(group0->getActorCount(), 8); - EXPECT_EQ(families[1]->getActorCount(), 8); - EXPECT_EQ(ftrack2.actors.size(), 8); - EXPECT_EQ(group1->getActorCount(), 8); - - // damage till the end, aggressively - std::default_random_engine re; - { - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); - NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; - - std::vector actors; - while (1) - { - TEST_ZONE_BEGIN("damage loop"); - uint32_t n0 = families[0]->getActorCount(); - uint32_t n1 = families[1]->getActorCount(); - actors.resize(n0 + n1); - families[0]->getActors(actors.data(), n0, 0); - families[1]->getActors(actors.data() + n0, n1, 0); - - bool workTBD = false; - for (TkActor* actor : actors) - { - if (!NvBlastActorCanFracture(actor->getActorLL(), nullptr)) - { - continue; - } - - workTBD = true; - - if (actor->getGraphNodeCount() > 1) - { - actor->damage(getFalloffProgram(), &radialDamageParams); - } - else - { - actor->damage(getShearProgram(), &shearDamageParams); - } - - if (re() % 1000 < 500) - { - // switch group - TkGroup* newGroup = actor->getGroup() == group0 ? group1 : group0; - actor->removeFromGroup(); - newGroup->addActor(*actor); - } - } - TEST_ZONE_END("damage loop"); - - if (!workTBD) - break; - - // async process 2 groups - { - EXPECT_GT(gtm1.process(2), (uint32_t)0); - EXPECT_GT(gtm0.process(2), (uint32_t)0); - uint32_t completed = 0; - while (completed < 2) - { - if (gtm1.wait(false)) - completed++; - if (gtm0.wait(false)) - completed++; - } - } - } - } - - // checks - EXPECT_EQ(families[0]->getActorCount(), ftrack1.actors.size()); - EXPECT_EQ(families[1]->getActorCount(), ftrack2.actors.size()); - EXPECT_EQ(65536, families[0]->getActorCount() + families[1]->getActorCount()); - EXPECT_EQ(65536, group0->getActorCount() + group1->getActorCount()); - - gtm0.release(); - gtm1.release(); - - group0->release(); - group1->release(); - - for (auto f : families) - f->release(); - - releaseTestAssets(); - releaseFramework(); -} - -TEST_F(TkTestStrict, ActorDamageBufferedDamage) -{ - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - - // group - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - // random engine - std::default_random_engine re; - - // cube asset - GeneratorAsset cube; - TkAssetDesc assetDesc; - generateCube(cube, assetDesc, 4, 2, 3); - assetDesc.bondFlags = nullptr; - TkAsset* cubeAsset = fwk->createAsset(assetDesc); - testAssets.push_back(cubeAsset); - - // actor desc - TkActorDesc cubeAD(cubeAsset); - - // test will be repated 'trials' times. Because of random shuffle inside. - const uint32_t trials = 100; - for (uint32_t i = 0; i < trials; i++) - { - // create actor - TkActor* actor = fwk->createActor(cubeAD); - EXPECT_TRUE(actor != nullptr); - TkFamily* family = (&actor->getFamily()); - group->addActor(*actor); - - // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors - // damage 4 corners with falloff radial 4 * 2 = 8 actors - // total 16 actors - uint32_t expectedActorCount = 16; - - // fallof params - const float P = 0.5f; - const float R = 0.35f; - - // 2 of damage types would be through user's NvBlastDamageProgram, this pointer must live till group->sync() - NvBlastExtRadialDamageDesc userR0 = getRadialDamageDesc(P, P, 0, R, R); - NvBlastExtProgramParams userProgramParams0 = { &userR0, nullptr }; - - NvBlastExtRadialDamageDesc userR1 = getRadialDamageDesc(-P, P, 0, R, R); - NvBlastExtProgramParams userProgramParams1 = { &userR1, nullptr }; - - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, -P, 0, R, R); - NvBlastExtProgramParams rDamageParams0 = { &r0, nullptr }; - NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, -P, 0, R, R); - NvBlastExtProgramParams rDamageParams1 = { &r1, nullptr }; - - - // fill damage functions, shuffle and apply - { - - const uint32_t damageCount = 7; - std::vector> damageFns(damageCount); - damageFns[0] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams0); }; - damageFns[1] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams1); }; - damageFns[2] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams2); }; - damageFns[3] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams0); }; - damageFns[4] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams1); }; - damageFns[5] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams0); }; - damageFns[6] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams1); }; - - // shuffle order! - std::shuffle(std::begin(damageFns), std::end(damageFns), re); - - for (uint32_t i = 0; i < damageCount; i++) - { - damageFns[i](); - } - } - - // sync - EXPECT_GT(m_groupTM->process(), (uint32_t)0); - m_groupTM->wait(); - - const auto ac = family->getActorCount(); - - // check - EXPECT_EQ(family->getActorCount(), expectedActorCount); - EXPECT_EQ(group->getActorCount(), expectedActorCount); - - // release - std::vector actors(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - for (auto a : actors) - a->removeFromGroup(); - family->release(); - } - - group->release(); - releaseFramework(); -} - -TEST_F(TkTestStrict, CreateActor) -{ - createFramework(); - TkFramework* framework = NvBlastTkFrameworkGet(); - - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector assets(assetDescCount); - - // assets - for (uint32_t i = 0; i < assetDescCount; ++i) - { - TkAssetDesc desc; - reinterpret_cast(desc) = g_assetDescs[i]; - desc.bondFlags = nullptr; - assets[i] = framework->createAsset(desc); - EXPECT_TRUE(assets[i] != nullptr); - } - - // actors - std::vector actors;; - std::vector actorFamilies;; - for (const TkAsset* asset : assets) - { - for (int i = 0; i < 2; i++) - { - TkActorDesc desc(asset); - TkActor* actor = framework->createActor(desc); - EXPECT_TRUE(actor != nullptr); - EXPECT_TRUE(actor->getActorLL() != nullptr); - //EXPECT_TRUE(&actor->getFamily() != nullptr); - EXPECT_TRUE(actor->getFamily().getActorCount() == 1); - actors.push_back(actor); - EXPECT_TRUE(std::find(actorFamilies.begin(), actorFamilies.end(), &actor->getFamily()) == actorFamilies.end()); - actorFamilies.push_back(&actor->getFamily()); - - } - } - - // framework checks - { - std::vector objects; - - // assets - { - const TkType* assetType = framework->getType(TkTypeIndex::Asset); - objects.resize(framework->getObjectCount(*assetType)); - EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *assetType) == static_cast(objects.size())); - ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)assets.data(), assets.size()); - } - - // actors -# if(0) // framework does not track actors explicitly anymore - { - const TkType* actorType = framework->getType(TkTypeIndex::Actor); - objects.resize(framework->getObjectCount(*actorType)); - EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), objects.size(), *actorType) == objects.size()); - ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actors.data(), actors.size()); - } -# endif - // families - { - const TkType* familyType = framework->getType(TkTypeIndex::Family); - objects.resize(framework->getObjectCount(*familyType)); - EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *familyType) == static_cast(objects.size())); - ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actorFamilies.data(), actorFamilies.size()); - } - } - - // release - for (TkActor* actor : actors) - { - actor->release(); - } - for (TkAsset* asset : assets) - { - asset->release(); - } - - releaseFramework(); -} - -template -TkFamily* TkBaseTest::familySerialization(TkFamily* family) -{ -#if 0 - TkFramework* fw = NvBlastTkFrameworkGet(); - - const TkType* familyType = fw->getType(TkTypeIndex::Family); - EXPECT_TRUE(familyType != nullptr); - - PsMemoryBuffer* membuf = PX_NEW(PsMemoryBuffer); - EXPECT_TRUE(membuf != nullptr); - if (membuf != nullptr) - { - const bool result = family->serialize(*membuf); - EXPECT_EQ(true, result); - if (!result) - { - return family; - } - const size_t familyActorCount = family->getActorCount(); - const TkAsset* familyAsset = family->getAsset(); - family->release(); - family = reinterpret_cast(fw->deserialize(*membuf)); - EXPECT_TRUE(family != nullptr); - if (family != nullptr) - { - EXPECT_EQ(familyActorCount, family->getActorCount()); - EXPECT_EQ(familyAsset, family->getAsset()); - } - membuf->release(); - } - - return family; -#endif - return nullptr; -} - -TEST_F(TkTestAllowWarnings, DISABLED_FamilySerialization) -{ - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - - // group - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - // random engine - std::default_random_engine re; - - // cube asset - TkAsset* cubeAsset = createCubeAsset(4, 2, 3, false); - - // actor desc - TkActorDesc cubeAD(cubeAsset); - - // create actor - TkActor* actor = fwk->createActor(cubeAD); - EXPECT_TRUE(actor != nullptr); - TkFamily* family = (&actor->getFamily()); - - // set an ID - NvBlastID id; - memcpy(id.data, "Observer-expectancy effect", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) - cubeAsset->setID(id); - - // serialize/deserialize - family = familySerialization(family); - - // fill damage functions, apply one by one and serialize family in between - { - // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors - // damage 4 corners with falloff radial 4 * 2 = 8 actors - // total 16 actors - uint32_t expectedActorCount = 16; - - // cube slicer params - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - // fallof params - const float P = 0.5f; - const float R = 0.35f; - NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, P, 0, R, R); - NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, P, 0, R, R); - NvBlastExtRadialDamageDesc r2 = getRadialDamageDesc(P, -P, 0, R, R); - NvBlastExtRadialDamageDesc r3 = getRadialDamageDesc(-P, -P, 0, R, R); - NvBlastExtProgramParams r0p = { &r0, nullptr }; - NvBlastExtProgramParams r1p = { &r1, nullptr }; - NvBlastExtProgramParams r2p = { &r2, nullptr }; - NvBlastExtProgramParams r3p = { &r3, nullptr }; - - const uint32_t damageCount = 7; - std::vector> damageFns(damageCount); - damageFns[0] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams0); }; - damageFns[1] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams1); }; - damageFns[2] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams2); }; - damageFns[3] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r0p); }; - damageFns[4] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r1p); }; - damageFns[5] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r2p); }; - damageFns[6] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r3p); }; - - std::vector actors(64); - - for (uint32_t i = 0; i < damageCount; i++) - { - actors.resize(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - - // damage - for (auto actor : actors) - { - group->addActor(*actor); - damageFns[i](actor); - } - - // sync - EXPECT_GT(m_groupTM->process(), (uint32_t)0); - m_groupTM->wait(); - - family = familySerialization(family); - } - - // check - EXPECT_EQ(family->getActorCount(), expectedActorCount); - } - - // release - family->release(); - - group->release(); - releaseFramework(); -} - -TEST_F(TkTestStrict, GroupStats) -{ - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - - // group - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - TkAsset* cubeAsset = createCubeAsset(4, 2); - TkActorDesc cubeDesc(cubeAsset); - - TkActor* cubeActor1 = fwk->createActor(cubeDesc); - TkActor* cubeActor2 = fwk->createActor(cubeDesc); - TkActor* cubeActor3 = fwk->createActor(cubeDesc); - TkActor* cubeActor4 = fwk->createActor(cubeDesc); - - group->addActor(*cubeActor1); - group->addActor(*cubeActor2); - group->addActor(*cubeActor3); - group->addActor(*cubeActor4); - - NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(0.0f, 0.0f, 0.0f); - NvBlastExtProgramParams radialDamageParams = { &r0, nullptr }; - cubeActor1->damage(getFalloffProgram(), &radialDamageParams); - cubeActor2->damage(getFalloffProgram(), &radialDamageParams); - cubeActor3->damage(getFalloffProgram(), &radialDamageParams); - cubeActor4->damage(getFalloffProgram(), &radialDamageParams); - - Nv::Blast::Time time; - m_groupTM->process(); - m_groupTM->wait(); - int64_t groupTime = time.getElapsedTicks(); - - TkGroupStats gstats; - group->getStats(gstats); - - int64_t total = gstats.timers.fracture + gstats.timers.island + gstats.timers.material + gstats.timers.partition + gstats.timers.visibility; - -#if NV_PROFILE - EXPECT_GT(total, 0); // some values are reported - EXPECT_LT(groupTime, total); // total LL time is higher than group time - EXPECT_GT((double)gstats.workerTime / groupTime, 2.0); // expect some minimal speedup (including overhead) - EXPECT_EQ(4, gstats.processedActorsCount); // actors processed -#endif - - releaseFramework(); -} - -TEST_F(TkTestStrict, FractureReportSupport) -{ - createFramework(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - NvBlastChunkDesc chunkDescs[] = - { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'prnt' }, - { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'left' }, - { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'rght' }, - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor = fwk->createActor(actorDesc); - actor->userData = (void*)'root'; - - class Listener : public TkEventListener - { - void receive(const TkEvent* events, uint32_t eventCount) override - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkJointUpdateEvent::EVENT_TYPE: - FAIL() << "not expecting joints here"; - break; - - case TkFractureCommands::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - - // Group::sync still needed the family for SharedMemory management. - EXPECT_TRUE(nullptr != actor.family); - - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - } - break; - - case TkFractureEvents::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - } - break; - - case TkSplitEvent::EVENT_TYPE: - { - const TkSplitEvent* split = event.getPayload(); - - EXPECT_TRUE(nullptr != split->parentData.family); - EXPECT_EQ((void*)'root', split->parentData.userData); - EXPECT_EQ(0, split->parentData.index); - - EXPECT_EQ(2, split->numChildren); - EXPECT_EQ(1, split->children[0]->getVisibleChunkCount()); - - uint32_t visibleChunkIndex; - // child order is not mandatory - { - TkActor* a = split->children[0]; - a->getVisibleChunkIndices(&visibleChunkIndex, 1); - uint32_t li = a->getIndex(); - EXPECT_EQ(1, li); - EXPECT_EQ(split->parentData.family, &a->getFamily()); - EXPECT_EQ('left', a->getAsset()->getChunks()[visibleChunkIndex].userData); - } - - { - TkActor*a = split->children[1]; - a->getVisibleChunkIndices(&visibleChunkIndex, 1); - uint32_t ri = a->getIndex(); - EXPECT_EQ(2, ri); - EXPECT_EQ(split->parentData.family, &a->getFamily()); - EXPECT_EQ('rght', a->getAsset()->getChunks()[visibleChunkIndex].userData); - } - } - break; - - default: - FAIL() << "should not get here"; - } - } - } - } listener; - actor->getFamily().addListener(listener); - - // expected state for the original actor, see Listener - EXPECT_EQ((void*)'root', actor->userData); - EXPECT_EQ(0, actor->getIndex()); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor); - - // this will trigger hierarchical chunk fracture - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - releaseFramework(); -} - -TEST_F(TkTestStrict, FractureReportGraph) -{ - createFramework(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - NvBlastBond bondToBreak = { { 1, 0, 0 }, 1, { 0, 0, 0 }, 0 }; - NvBlastBond bondToKeep = { { 1, 0, 0 }, 1, { 10, 10, 10 }, 0 }; - NvBlastBondDesc bondDescs[] = - { - { bondToKeep, { 1, 2 } }, - { bondToBreak, { 2, 3 } }, - }; - - NvBlastChunkDesc chunkDescs[] = - { - { { 0, 0, 0 }, 2, UINT32_MAX, NvBlastChunkDesc::NoFlags, 'root' }, - { { -1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'A' }, - { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'B' }, - { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'C' }, - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 2; - assetDesc.bondDescs = bondDescs; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* rootActor = fwk->createActor(actorDesc); - rootActor->userData = (void*)'root'; - - class Listener : public TkEventListener - { - void receive(const TkEvent* events, uint32_t eventCount) override - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkJointUpdateEvent::EVENT_TYPE: - FAIL() << "not expecting joints here"; - break; - - case TkFractureCommands::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - - // Group::sync still needed the family for SharedMemory management. - EXPECT_TRUE(nullptr != actor.family); - - // original actor state is not preserved, the last test will fail - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - - // this information was invalid anyway - //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; - } - break; - - case TkFractureEvents::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - - // Group::sync still needed the family for SharedMemory management. - EXPECT_TRUE(nullptr != actor.family); - - // original actor state is not preserved, the last test will fail - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - - // this information was invalid anyway - //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; - } - break; - - case TkSplitEvent::EVENT_TYPE: - { - const TkSplitEvent* split = event.getPayload(); - EXPECT_EQ((void*)'root', split->parentData.userData); - EXPECT_EQ(0, split->parentData.index); - EXPECT_EQ(2, split->numChildren); - - uint32_t visibleChunkIndex[2]; - // child order is not mandatory - { - TkActor* a = split->children[1]; - EXPECT_EQ(2, a->getVisibleChunkCount()); // chunks A and B - a->getVisibleChunkIndices(visibleChunkIndex, 2); - uint32_t actorIndex = a->getIndex(); - EXPECT_EQ(0, actorIndex); // same index as the original actor - - // visible chunk order is not mandatory - EXPECT_EQ('B', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); - EXPECT_EQ('A', a->getAsset()->getChunks()[visibleChunkIndex[1]].userData); - } - - { - TkActor* a = split->children[0]; - EXPECT_EQ(1, a->getVisibleChunkCount()); - a->getVisibleChunkIndices(visibleChunkIndex, 1); - uint32_t actorIndex = a->getIndex(); - EXPECT_EQ(2, actorIndex); - EXPECT_EQ('C', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); - } - } - break; - - default: - FAIL() << "should not get here"; - } - } - } - } listener; - rootActor->getFamily().addListener(listener); - - // expected state for the original actor, see Listener - EXPECT_EQ((void*)'root', rootActor->userData); - EXPECT_EQ(0, rootActor->getIndex()); - EXPECT_EQ(1, rootActor->getVisibleChunkCount()); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*rootActor); - - // this will trigger one bond to break - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0, 0.5f, 0.5f); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - rootActor->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - releaseFramework(); -} - -TEST_F(TkTestStrict, SplitWarning) // GWD-167 -{ - createFramework(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - NvBlastChunkDesc chunkDescs[] = - { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' }, - { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'A' }, - { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'B' }, - { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'C' }, - { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'D' }, - { { -1,0,0 }, 1, 1, NvBlastChunkDesc::NoFlags, 'AAAA' }, - { { +1,0,0 }, 1, 2, NvBlastChunkDesc::NoFlags, 'BBBB' }, - { { -1,0,0 }, 1, 3, NvBlastChunkDesc::NoFlags, 'CCCC' }, - { { +1,0,0 }, 1, 4, NvBlastChunkDesc::NoFlags, 'DDDD' }, - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor = fwk->createActor(actorDesc); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - releaseFramework(); -} - - -TEST_F(TkTestAllowWarnings, ChangeThreadCountToZero) -{ - // tests that group still allocates memory for one worker - // by replacing to a 0 threads cpu dispatcher (warns) - // mainly relies on internal asserts - - class EventCounter : public TkEventListener - { - public: - EventCounter() :fracCommands(0), fracEvents(0) {} - - void receive(const TkEvent* events, uint32_t eventCount) - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkFractureCommands::EVENT_TYPE: - fracCommands++; - break; - case TkFractureEvents::EVENT_TYPE: - fracEvents++; - break; - default: - FAIL(); - // no split due to single chunk - // no joints - } - } - } - - uint32_t fracCommands, fracEvents; - } listener; - - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - NvBlastChunkDesc chunkDescs[] = { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor1 = fwk->createActor(actorDesc); - TkActor* actor2 = fwk->createActor(actorDesc); - TkActor* actor3 = fwk->createActor(actorDesc); - TkActor* actor4 = fwk->createActor(actorDesc); - - actor1->getFamily().addListener(listener); - actor2->getFamily().addListener(listener); - actor3->getFamily().addListener(listener); - actor4->getFamily().addListener(listener); - -#if USE_PHYSX_DISPATCHER - PxU32 affinity[] = { 1, 2, 4, 8 }; - PxDefaultCpuDispatcher* disp0 = PxDefaultCpuDispatcherCreate(0, affinity); - disp0->setRunProfiled(false); - PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); - disp4->setRunProfiled(false); -#else - TestCpuDispatcher* disp0 = new TestCpuDispatcher(0); - TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); -#endif - - m_taskman->setCpuDispatcher(*disp4); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor1); - group->addActor(*actor2); - m_taskman->setCpuDispatcher(*disp0); - //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); - group->addActor(*actor3); - group->addActor(*actor4); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor1->damage(getFalloffProgram(), &radialDamageParams); - actor2->damage(getFalloffProgram(), &radialDamageParams); - actor3->damage(getFalloffProgram(), &radialDamageParams); - actor4->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_EQ(4, listener.fracCommands); - EXPECT_EQ(4, listener.fracEvents); - - releaseFramework(); - - disp0->release(); - disp4->release(); -} - -TEST_F(TkTestStrict, ChangeThreadCountUp) -{ - // tests that group allocates more memory for additional workers - // by replacing to a higher thread count cpu dispatcher (warns) - // mainly relies on internal asserts - - class EventCounter : public TkEventListener - { - public: - EventCounter() :fracCommands(0), fracEvents(0) {} - - void receive(const TkEvent* events, uint32_t eventCount) - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkFractureCommands::EVENT_TYPE: - fracCommands++; - break; - case TkFractureEvents::EVENT_TYPE: - fracEvents++; - break; - default: - FAIL(); - // no split due to single chunk - // no joints - } - } - } - - uint32_t fracCommands, fracEvents; - } listener; - - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - NvBlastChunkDesc chunkDescs[] = { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor1 = fwk->createActor(actorDesc); - TkActor* actor2 = fwk->createActor(actorDesc); - TkActor* actor3 = fwk->createActor(actorDesc); - TkActor* actor4 = fwk->createActor(actorDesc); - - actor1->getFamily().addListener(listener); - actor2->getFamily().addListener(listener); - actor3->getFamily().addListener(listener); - actor4->getFamily().addListener(listener); - -#if USE_PHYSX_DISPATCHER - PxU32 affinity[] = { 1, 2, 4, 8 }; - PxDefaultCpuDispatcher* disp2 = PxDefaultCpuDispatcherCreate(2, affinity); - disp2->setRunProfiled(false); - PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); - disp4->setRunProfiled(false); -#else - TestCpuDispatcher* disp2 = new TestCpuDispatcher(2); - TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); -#endif - - m_taskman->setCpuDispatcher(*disp2); - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor1); - group->addActor(*actor2); - group->addActor(*actor3); - group->addActor(*actor4); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor1->damage(getFalloffProgram(), &radialDamageParams); - actor2->damage(getFalloffProgram(), &radialDamageParams); - actor3->damage(getFalloffProgram(), &radialDamageParams); - actor4->damage(getFalloffProgram(), &radialDamageParams); - - m_taskman->setCpuDispatcher(*disp4); - //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_EQ(4, listener.fracCommands); - EXPECT_EQ(4, listener.fracEvents); - - releaseFramework(); - - disp2->release(); - disp4->release(); -} - -TEST_F(TkTestAllowWarnings, GroupNoWorkers) -{ - // tests that group still works without a taskmanager - // a warnings is expected - // mainly relies on internal asserts - - class EventCounter : public TkEventListener - { - public: - EventCounter() :fracCommands(0), fracEvents(0) {} - - void receive(const TkEvent* events, uint32_t eventCount) - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkFractureCommands::EVENT_TYPE: - fracCommands++; - break; - case TkFractureEvents::EVENT_TYPE: - fracEvents++; - break; - default: - FAIL(); - // no split due to single chunk - // no joints - } - } - } - - uint32_t fracCommands, fracEvents; - } listener; - - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - NvBlastChunkDesc chunkDescs[] = { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor1 = fwk->createActor(actorDesc); - TkActor* actor2 = fwk->createActor(actorDesc); - TkActor* actor3 = fwk->createActor(actorDesc); - TkActor* actor4 = fwk->createActor(actorDesc); - - actor1->getFamily().addListener(listener); - actor2->getFamily().addListener(listener); - actor3->getFamily().addListener(listener); - actor4->getFamily().addListener(listener); - -#if USE_PHYSX_DISPATCHER - PxDefaultCpuDispatcher* disp = PxDefaultCpuDispatcherCreate(0); -#else - TestCpuDispatcher* disp = new TestCpuDispatcher(0); -#endif - m_taskman->setCpuDispatcher(*disp); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor1); - group->addActor(*actor2); - group->addActor(*actor3); - group->addActor(*actor4); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams programParams = { - &radialDamage, - getDefaultMaterial() - }; - - actor1->damage(getFalloffProgram(), &programParams); - actor2->damage(getFalloffProgram(), &programParams); - actor3->damage(getFalloffProgram(), &programParams); - actor4->damage(getFalloffProgram(), &programParams); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_EQ(4, listener.fracCommands); - EXPECT_EQ(4, listener.fracEvents); - - disp->release(); - - releaseFramework(); -} - +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "TkBaseTest.h" + +#include +#include +#include + +#include "PsMemoryBuffer.h" + +#include "NvBlastTime.h" + +#include "NvBlastExtPxTask.h" + +struct ExpectedVisibleChunks +{ + ExpectedVisibleChunks() :numActors(0), numChunks(0) {} + ExpectedVisibleChunks(size_t a, size_t c) :numActors(a), numChunks(c) {} + size_t numActors; size_t numChunks; +}; + +void testResults(std::vector& families, std::map& expectedVisibleChunks) +{ + size_t numActors = 0; + for (TkFamily* fam : families) + { + auto ex = expectedVisibleChunks[fam]; + EXPECT_EQ(ex.numActors, fam->getActorCount()); + numActors += ex.numActors; + std::vector actors(fam->getActorCount()); + fam->getActors(actors.data(), static_cast(actors.size())); + for (TkActor* actor : actors) + { + EXPECT_EQ(ex.numChunks, actor->getVisibleChunkCount()); + } + } + + size_t numActorsExpected = 0; + for (auto expected : expectedVisibleChunks) + { + numActorsExpected += expected.second.numActors; + } + + EXPECT_EQ(numActorsExpected, numActors); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tests +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +TEST_F(TkTestStrict, CreateFramework) +{ + createFramework(); + releaseFramework(); +} + +TEST_F(TkTestStrict, CreateAsset) +{ + createFramework(); + + createTestAssets(); + releaseTestAssets(); + + releaseFramework(); +} + +#if USE_PHYSX_DISPATCHER +TEST_F(TkTestStrict, DISABLED_MemLeak) +{ + PxFoundation* pxFoundation = PxCreateFoundation(PX_FOUNDATION_VERSION, NvBlastGetPxAllocatorCallback(), NvBlastGetPxErrorCallback()); + PxU32 affinity[] = { 1, 2, 4, 8 }; + PxDefaultCpuDispatcher* cpuDispatcher = PxDefaultCpuDispatcherCreate(4, affinity); + cpuDispatcher->setRunProfiled(false); + PxTaskManager* taskman = PxTaskManager::createTaskManager(NvBlastGetPxErrorCallback(), cpuDispatcher, nullptr); + + cpuDispatcher->release(); + taskman->release(); + pxFoundation->release(); +} +#endif + +TEST_F(TkTestStrict, ActorDamageNoGroup) +{ + createFramework(); + createTestAssets(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + TkActorDesc actorDesc; + actorDesc.asset = testAssets[0]; + TkActor* actor = fwk->createActor(actorDesc); + + const size_t bondFractureCount = 4; + NvBlastFractureBuffers commands; + NvBlastBondFractureData bdata[bondFractureCount]; + for (uint32_t i = 0; i < bondFractureCount; i++) + { + bdata[i].nodeIndex0 = 2 * i + 0; + bdata[i].nodeIndex1 = 2 * i + 1; + bdata[i].health = 1.0f; + } + commands.bondFractureCount = bondFractureCount; + commands.bondFractures = bdata; + commands.chunkFractureCount = 0; + commands.chunkFractures = nullptr; + actor->applyFracture(&commands, &commands); + + TkFamily& family = actor->getFamily(); + + EXPECT_TRUE(commands.bondFractureCount == 4); + EXPECT_TRUE(actor->isPending()); + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + group->addActor(*actor); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_FALSE(actor->isPending()); + EXPECT_EQ(2, family.getActorCount()); + + releaseFramework(); +} + +TEST_F(TkTestStrict, ActorDamageGroup) +{ + TEST_ZONE_BEGIN("ActorDamageGroup"); + + createFramework(); + createTestAssets(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + TestFamilyTracker ftrack1, ftrack2; + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + + NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); + NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; + + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + std::vector families; + TkFamily* trackedFamily; + std::map expectedVisibleChunks; + + { + TkActorDesc adesc(testAssets[0]); + + TkActor* actor1 = fwk->createActor(adesc); + EXPECT_TRUE(actor1 != nullptr); + + TkActor* actor2 = fwk->createActor(adesc); + EXPECT_TRUE(actor2 != nullptr); + + + expectedVisibleChunks[&actor1->getFamily()] = ExpectedVisibleChunks(8, 1); // full damage + expectedVisibleChunks[&actor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split + + GeneratorAsset cube; + TkAssetDesc assetDesc; + generateCube(cube, assetDesc, 5, 2); + assetDesc.bondFlags = nullptr; + + TkAsset* cubeAsset = fwk->createAsset(assetDesc); + testAssets.push_back(cubeAsset); + + TkActorDesc cubeAD(cubeAsset); + + TkActor* cubeActor1 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor1 != nullptr); + + trackedFamily = &cubeActor1->getFamily(); + cubeActor1->getFamily().addListener(ftrack1); + + TkActor* cubeActor2 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor2 != nullptr); + + expectedVisibleChunks[&cubeActor1->getFamily()] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each + expectedVisibleChunks[&cubeActor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split + + ftrack1.insertActor(cubeActor1); + ftrack2.insertActor(actor1); + + actor1->getFamily().addListener(ftrack2); + + TEST_ZONE_BEGIN("add to groups"); + group->addActor(*cubeActor1); + group->addActor(*cubeActor2); + group->addActor(*actor1); + group->addActor(*actor2); + TEST_ZONE_END("add to groups"); + + families.push_back(&cubeActor1->getFamily()); + families.push_back(&cubeActor2->getFamily()); + families.push_back(&actor1->getFamily()); + families.push_back(&actor2->getFamily()); + + cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); + actor1->damage(getFalloffProgram(), &radialDamageParams); + } + + EXPECT_FALSE(group->endProcess()); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + for (TkActor* actor : actors) + { + actor->damage(getCubeSlicerProgram(), &csDamageParams1); + } + } + expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4, 2); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + for (TkActor* actor : actors) + { + actor->damage(getCubeSlicerProgram(), &csDamageParams2); + } + } + + expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(8, 1); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + TEST_ZONE_BEGIN("damage"); + for (TkActor* actor : actors) + { + actor->damage(getFalloffProgram(), &radialDamageParams); + } + TEST_ZONE_END("damage"); + } + expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4096, 1); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + TEST_ZONE_BEGIN("damage"); + for (TkActor* actor : actors) + { + actor->damage(getShearProgram(), &shearDamageParams); + } + TEST_ZONE_END("damage"); + } + + m_groupTM->process(); + m_groupTM->wait(); + + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + TEST_ZONE_BEGIN("damage"); + for (TkActor* actor : actors) + { + actor->damage(getShearProgram(), &shearDamageParams); + } + TEST_ZONE_END("damage"); + } + + m_groupTM->process(); + m_groupTM->wait(); + + group->release(); + + TEST_ZONE_BEGIN("family release"); + trackedFamily->release(); + TEST_ZONE_END("family release"); + + releaseTestAssets(); + releaseFramework(); + + TEST_ZONE_END("ActorDamageGroup"); +} + + +TEST_F(TkTestStrict, ActorDamageMultiGroup) +{ + createFramework(); + createTestAssets(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + TestFamilyTracker ftrack1, ftrack2; + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group0 = fwk->createGroup(gdesc); + EXPECT_TRUE(group0 != nullptr); + TkGroup* group1 = fwk->createGroup(gdesc); + EXPECT_TRUE(group1 != nullptr); + + ExtGroupTaskManager& gtm1 = *ExtGroupTaskManager::create(*m_taskman, group1); + ExtGroupTaskManager& gtm0 = *ExtGroupTaskManager::create(*m_taskman, group0); + + std::vector families(2); + std::map expectedVisibleChunks; + + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + // prepare 2 equal actors/families and damage + { + GeneratorAsset cube; + TkAssetDesc assetDesc; + generateCube(cube, assetDesc, 6, 2, 5); + assetDesc.bondFlags = nullptr; + + TkAsset* cubeAsset = fwk->createAsset(assetDesc); + testAssets.push_back(cubeAsset); + + TkActorDesc cubeAD(cubeAsset); + + TkActor* cubeActor0 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor0 != nullptr); + cubeActor0->getFamily().addListener(ftrack1); + + TkActor* cubeActor1 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor1 != nullptr); + cubeActor1->getFamily().addListener(ftrack2); + + ftrack1.insertActor(cubeActor0); + ftrack2.insertActor(cubeActor1); + + group0->addActor(*cubeActor0); + group1->addActor(*cubeActor1); + + families[0] = (&cubeActor0->getFamily()); + families[1] = (&cubeActor1->getFamily()); + + { + cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams0); + cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams1); + + cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); + } + + expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(4, 2); // split in 4, 2 chunks each + expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each + } + + // async process 2 groups + { + EXPECT_GT(gtm0.process(2), (uint32_t)0); + EXPECT_GT(gtm1.process(2), (uint32_t)0); + uint32_t completed = 0; + while (completed < 2) + { + if (gtm0.wait(false)) + completed++; + if (gtm1.wait(false)) + completed++; + } + } + + // checks + testResults(families, expectedVisibleChunks); + EXPECT_EQ(families[0]->getActorCount(), 4); + EXPECT_EQ(group0->getActorCount(), 4); + EXPECT_EQ(families[1]->getActorCount(), 2); + EXPECT_EQ(group1->getActorCount(), 2); + + // we have group0 with 4 actors 2 chunks: + // group0: [2]' [2]' [2]' [2]' (family0') + // group1: [4]'' [4]'' (family1'') + // rearrange: + // group0: [2]' [2]' [4]'' + // group1: [4]'' [2]' [2]' + { + TkActor* group0Actors[2]; + group0->getActors(group0Actors, 2, 1); // start index: 1, because..why not? + TkActor* group1Actors[2]; + group1->getActors(group1Actors, 2, 0); + group0Actors[0]->removeFromGroup(); + group1->addActor(*group0Actors[0]); + group0Actors[1]->removeFromGroup(); + group1->addActor(*group0Actors[1]); + group1Actors[0]->removeFromGroup(); + group0->addActor(*group1Actors[0]); + } + + // checks + EXPECT_EQ(families[0]->getActorCount(), 4); + EXPECT_EQ(group0->getActorCount(), 3); + EXPECT_EQ(families[1]->getActorCount(), 2); + EXPECT_EQ(group1->getActorCount(), 3); + + // damage all + { + TkActor* allActors[6]; + families[0]->getActors(allActors, 4, 0); + families[1]->getActors(allActors + 4, 2, 0); + + typedef std::pair pair; + std::set combinations; + for (auto actor : allActors) + { + combinations.emplace(pair(actor->getGroup(), &actor->getFamily())); + if (actor->getVisibleChunkCount() == 4) + { + actor->damage(getCubeSlicerProgram(), &csDamageParams1); + } + actor->damage(getCubeSlicerProgram(), &csDamageParams2); + } + EXPECT_EQ(combinations.size(), 4); + + expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each + expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each + } + + // async process 2 groups + { + EXPECT_GT(gtm1.process(2), (uint32_t)0); + EXPECT_GT(gtm0.process(2), (uint32_t)0); + uint32_t completed = 0; + while (completed < 2) + { + if (gtm1.wait(false)) + completed++; + if (gtm0.wait(false)) + completed++; + } + } + + // checks + testResults(families, expectedVisibleChunks); + EXPECT_EQ(families[0]->getActorCount(), 8); + EXPECT_EQ(ftrack1.actors.size(), 8); + EXPECT_EQ(group0->getActorCount(), 8); + EXPECT_EQ(families[1]->getActorCount(), 8); + EXPECT_EQ(ftrack2.actors.size(), 8); + EXPECT_EQ(group1->getActorCount(), 8); + + // damage till the end, aggressively + std::default_random_engine re; + { + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); + NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; + + std::vector actors; + while (1) + { + TEST_ZONE_BEGIN("damage loop"); + uint32_t n0 = families[0]->getActorCount(); + uint32_t n1 = families[1]->getActorCount(); + actors.resize(n0 + n1); + families[0]->getActors(actors.data(), n0, 0); + families[1]->getActors(actors.data() + n0, n1, 0); + + bool workTBD = false; + for (TkActor* actor : actors) + { + if (!NvBlastActorCanFracture(actor->getActorLL(), nullptr)) + { + continue; + } + + workTBD = true; + + if (actor->getGraphNodeCount() > 1) + { + actor->damage(getFalloffProgram(), &radialDamageParams); + } + else + { + actor->damage(getShearProgram(), &shearDamageParams); + } + + if (re() % 1000 < 500) + { + // switch group + TkGroup* newGroup = actor->getGroup() == group0 ? group1 : group0; + actor->removeFromGroup(); + newGroup->addActor(*actor); + } + } + TEST_ZONE_END("damage loop"); + + if (!workTBD) + break; + + // async process 2 groups + { + EXPECT_GT(gtm1.process(2), (uint32_t)0); + EXPECT_GT(gtm0.process(2), (uint32_t)0); + uint32_t completed = 0; + while (completed < 2) + { + if (gtm1.wait(false)) + completed++; + if (gtm0.wait(false)) + completed++; + } + } + } + } + + // checks + EXPECT_EQ(families[0]->getActorCount(), ftrack1.actors.size()); + EXPECT_EQ(families[1]->getActorCount(), ftrack2.actors.size()); + EXPECT_EQ(65536, families[0]->getActorCount() + families[1]->getActorCount()); + EXPECT_EQ(65536, group0->getActorCount() + group1->getActorCount()); + + gtm0.release(); + gtm1.release(); + + group0->release(); + group1->release(); + + for (auto f : families) + f->release(); + + releaseTestAssets(); + releaseFramework(); +} + +TEST_F(TkTestStrict, ActorDamageBufferedDamage) +{ + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + + // group + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + // random engine + std::default_random_engine re; + + // cube asset + GeneratorAsset cube; + TkAssetDesc assetDesc; + generateCube(cube, assetDesc, 4, 2, 3); + assetDesc.bondFlags = nullptr; + TkAsset* cubeAsset = fwk->createAsset(assetDesc); + testAssets.push_back(cubeAsset); + + // actor desc + TkActorDesc cubeAD(cubeAsset); + + // test will be repated 'trials' times. Because of random shuffle inside. + const uint32_t trials = 100; + for (uint32_t i = 0; i < trials; i++) + { + // create actor + TkActor* actor = fwk->createActor(cubeAD); + EXPECT_TRUE(actor != nullptr); + TkFamily* family = (&actor->getFamily()); + group->addActor(*actor); + + // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors + // damage 4 corners with falloff radial 4 * 2 = 8 actors + // total 16 actors + uint32_t expectedActorCount = 16; + + // fallof params + const float P = 0.5f; + const float R = 0.35f; + + // 2 of damage types would be through user's NvBlastDamageProgram, this pointer must live till group->sync() + NvBlastExtRadialDamageDesc userR0 = getRadialDamageDesc(P, P, 0, R, R); + NvBlastExtProgramParams userProgramParams0 = { &userR0, nullptr }; + + NvBlastExtRadialDamageDesc userR1 = getRadialDamageDesc(-P, P, 0, R, R); + NvBlastExtProgramParams userProgramParams1 = { &userR1, nullptr }; + + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, -P, 0, R, R); + NvBlastExtProgramParams rDamageParams0 = { &r0, nullptr }; + NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, -P, 0, R, R); + NvBlastExtProgramParams rDamageParams1 = { &r1, nullptr }; + + + // fill damage functions, shuffle and apply + { + + const uint32_t damageCount = 7; + std::vector> damageFns(damageCount); + damageFns[0] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams0); }; + damageFns[1] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams1); }; + damageFns[2] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams2); }; + damageFns[3] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams0); }; + damageFns[4] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams1); }; + damageFns[5] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams0); }; + damageFns[6] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams1); }; + + // shuffle order! + std::shuffle(std::begin(damageFns), std::end(damageFns), re); + + for (uint32_t i = 0; i < damageCount; i++) + { + damageFns[i](); + } + } + + // sync + EXPECT_GT(m_groupTM->process(), (uint32_t)0); + m_groupTM->wait(); + + const auto ac = family->getActorCount(); + + // check + EXPECT_EQ(family->getActorCount(), expectedActorCount); + EXPECT_EQ(group->getActorCount(), expectedActorCount); + + // release + std::vector actors(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + for (auto a : actors) + a->removeFromGroup(); + family->release(); + } + + group->release(); + releaseFramework(); +} + +TEST_F(TkTestStrict, CreateActor) +{ + createFramework(); + TkFramework* framework = NvBlastTkFrameworkGet(); + + const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + + std::vector assets(assetDescCount); + + // assets + for (uint32_t i = 0; i < assetDescCount; ++i) + { + TkAssetDesc desc; + reinterpret_cast(desc) = g_assetDescs[i]; + desc.bondFlags = nullptr; + assets[i] = framework->createAsset(desc); + EXPECT_TRUE(assets[i] != nullptr); + } + + // actors + std::vector actors;; + std::vector actorFamilies;; + for (const TkAsset* asset : assets) + { + for (int i = 0; i < 2; i++) + { + TkActorDesc desc(asset); + TkActor* actor = framework->createActor(desc); + EXPECT_TRUE(actor != nullptr); + EXPECT_TRUE(actor->getActorLL() != nullptr); + //EXPECT_TRUE(&actor->getFamily() != nullptr); + EXPECT_TRUE(actor->getFamily().getActorCount() == 1); + actors.push_back(actor); + EXPECT_TRUE(std::find(actorFamilies.begin(), actorFamilies.end(), &actor->getFamily()) == actorFamilies.end()); + actorFamilies.push_back(&actor->getFamily()); + + } + } + + // framework checks + { + std::vector objects; + + // assets + { + const TkType* assetType = framework->getType(TkTypeIndex::Asset); + objects.resize(framework->getObjectCount(*assetType)); + EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *assetType) == static_cast(objects.size())); + ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)assets.data(), assets.size()); + } + + // actors +# if(0) // framework does not track actors explicitly anymore + { + const TkType* actorType = framework->getType(TkTypeIndex::Actor); + objects.resize(framework->getObjectCount(*actorType)); + EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), objects.size(), *actorType) == objects.size()); + ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actors.data(), actors.size()); + } +# endif + // families + { + const TkType* familyType = framework->getType(TkTypeIndex::Family); + objects.resize(framework->getObjectCount(*familyType)); + EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *familyType) == static_cast(objects.size())); + ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actorFamilies.data(), actorFamilies.size()); + } + } + + // release + for (TkActor* actor : actors) + { + actor->release(); + } + for (TkAsset* asset : assets) + { + asset->release(); + } + + releaseFramework(); +} + +template +TkFamily* TkBaseTest::familySerialization(TkFamily* family) +{ +#if 0 + TkFramework* fw = NvBlastTkFrameworkGet(); + + const TkType* familyType = fw->getType(TkTypeIndex::Family); + EXPECT_TRUE(familyType != nullptr); + + PsMemoryBuffer* membuf = PX_NEW(PsMemoryBuffer); + EXPECT_TRUE(membuf != nullptr); + if (membuf != nullptr) + { + const bool result = family->serialize(*membuf); + EXPECT_EQ(true, result); + if (!result) + { + return family; + } + const size_t familyActorCount = family->getActorCount(); + const TkAsset* familyAsset = family->getAsset(); + family->release(); + family = reinterpret_cast(fw->deserialize(*membuf)); + EXPECT_TRUE(family != nullptr); + if (family != nullptr) + { + EXPECT_EQ(familyActorCount, family->getActorCount()); + EXPECT_EQ(familyAsset, family->getAsset()); + } + membuf->release(); + } + + return family; +#endif + return nullptr; +} + +TEST_F(TkTestAllowWarnings, DISABLED_FamilySerialization) +{ + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + + // group + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + // random engine + std::default_random_engine re; + + // cube asset + TkAsset* cubeAsset = createCubeAsset(4, 2, 3, false); + + // actor desc + TkActorDesc cubeAD(cubeAsset); + + // create actor + TkActor* actor = fwk->createActor(cubeAD); + EXPECT_TRUE(actor != nullptr); + TkFamily* family = (&actor->getFamily()); + + // set an ID + NvBlastID id; + memcpy(id.data, "Observer-expectancy effect", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) + cubeAsset->setID(id); + + // serialize/deserialize + family = familySerialization(family); + + // fill damage functions, apply one by one and serialize family in between + { + // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors + // damage 4 corners with falloff radial 4 * 2 = 8 actors + // total 16 actors + uint32_t expectedActorCount = 16; + + // cube slicer params + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + // fallof params + const float P = 0.5f; + const float R = 0.35f; + NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, P, 0, R, R); + NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, P, 0, R, R); + NvBlastExtRadialDamageDesc r2 = getRadialDamageDesc(P, -P, 0, R, R); + NvBlastExtRadialDamageDesc r3 = getRadialDamageDesc(-P, -P, 0, R, R); + NvBlastExtProgramParams r0p = { &r0, nullptr }; + NvBlastExtProgramParams r1p = { &r1, nullptr }; + NvBlastExtProgramParams r2p = { &r2, nullptr }; + NvBlastExtProgramParams r3p = { &r3, nullptr }; + + const uint32_t damageCount = 7; + std::vector> damageFns(damageCount); + damageFns[0] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams0); }; + damageFns[1] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams1); }; + damageFns[2] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams2); }; + damageFns[3] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r0p); }; + damageFns[4] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r1p); }; + damageFns[5] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r2p); }; + damageFns[6] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r3p); }; + + std::vector actors(64); + + for (uint32_t i = 0; i < damageCount; i++) + { + actors.resize(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + + // damage + for (auto actor : actors) + { + group->addActor(*actor); + damageFns[i](actor); + } + + // sync + EXPECT_GT(m_groupTM->process(), (uint32_t)0); + m_groupTM->wait(); + + family = familySerialization(family); + } + + // check + EXPECT_EQ(family->getActorCount(), expectedActorCount); + } + + // release + family->release(); + + group->release(); + releaseFramework(); +} + +TEST_F(TkTestStrict, GroupStats) +{ + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + + // group + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + TkAsset* cubeAsset = createCubeAsset(4, 2); + TkActorDesc cubeDesc(cubeAsset); + + TkActor* cubeActor1 = fwk->createActor(cubeDesc); + TkActor* cubeActor2 = fwk->createActor(cubeDesc); + TkActor* cubeActor3 = fwk->createActor(cubeDesc); + TkActor* cubeActor4 = fwk->createActor(cubeDesc); + + group->addActor(*cubeActor1); + group->addActor(*cubeActor2); + group->addActor(*cubeActor3); + group->addActor(*cubeActor4); + + NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(0.0f, 0.0f, 0.0f); + NvBlastExtProgramParams radialDamageParams = { &r0, nullptr }; + cubeActor1->damage(getFalloffProgram(), &radialDamageParams); + cubeActor2->damage(getFalloffProgram(), &radialDamageParams); + cubeActor3->damage(getFalloffProgram(), &radialDamageParams); + cubeActor4->damage(getFalloffProgram(), &radialDamageParams); + + Nv::Blast::Time time; + m_groupTM->process(); + m_groupTM->wait(); + int64_t groupTime = time.getElapsedTicks(); + + TkGroupStats gstats; + group->getStats(gstats); + + int64_t total = gstats.timers.fracture + gstats.timers.island + gstats.timers.material + gstats.timers.partition + gstats.timers.visibility; + +#if NV_PROFILE + EXPECT_GT(total, 0); // some values are reported + EXPECT_LT(groupTime, total); // total LL time is higher than group time + EXPECT_GT((double)gstats.workerTime / groupTime, 2.0); // expect some minimal speedup (including overhead) + EXPECT_EQ(4, gstats.processedActorsCount); // actors processed +#endif + + releaseFramework(); +} + +TEST_F(TkTestStrict, FractureReportSupport) +{ + createFramework(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + NvBlastChunkDesc chunkDescs[] = + { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'prnt' }, + { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'left' }, + { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'rght' }, + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor = fwk->createActor(actorDesc); + actor->userData = (void*)'root'; + + class Listener : public TkEventListener + { + void receive(const TkEvent* events, uint32_t eventCount) override + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkJointUpdateEvent::EVENT_TYPE: + FAIL() << "not expecting joints here"; + break; + + case TkFractureCommands::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + + // Group::sync still needed the family for SharedMemory management. + EXPECT_TRUE(nullptr != actor.family); + + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + } + break; + + case TkFractureEvents::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + } + break; + + case TkSplitEvent::EVENT_TYPE: + { + const TkSplitEvent* split = event.getPayload(); + + EXPECT_TRUE(nullptr != split->parentData.family); + EXPECT_EQ((void*)'root', split->parentData.userData); + EXPECT_EQ(0, split->parentData.index); + + EXPECT_EQ(2, split->numChildren); + EXPECT_EQ(1, split->children[0]->getVisibleChunkCount()); + + uint32_t visibleChunkIndex; + // child order is not mandatory + { + TkActor* a = split->children[0]; + a->getVisibleChunkIndices(&visibleChunkIndex, 1); + uint32_t li = a->getIndex(); + EXPECT_EQ(1, li); + EXPECT_EQ(split->parentData.family, &a->getFamily()); + EXPECT_EQ('left', a->getAsset()->getChunks()[visibleChunkIndex].userData); + } + + { + TkActor*a = split->children[1]; + a->getVisibleChunkIndices(&visibleChunkIndex, 1); + uint32_t ri = a->getIndex(); + EXPECT_EQ(2, ri); + EXPECT_EQ(split->parentData.family, &a->getFamily()); + EXPECT_EQ('rght', a->getAsset()->getChunks()[visibleChunkIndex].userData); + } + } + break; + + default: + FAIL() << "should not get here"; + } + } + } + } listener; + actor->getFamily().addListener(listener); + + // expected state for the original actor, see Listener + EXPECT_EQ((void*)'root', actor->userData); + EXPECT_EQ(0, actor->getIndex()); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor); + + // this will trigger hierarchical chunk fracture + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + releaseFramework(); +} + +TEST_F(TkTestStrict, FractureReportGraph) +{ + createFramework(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + NvBlastBond bondToBreak = { { 1, 0, 0 }, 1, { 0, 0, 0 }, 0 }; + NvBlastBond bondToKeep = { { 1, 0, 0 }, 1, { 10, 10, 10 }, 0 }; + NvBlastBondDesc bondDescs[] = + { + { bondToKeep, { 1, 2 } }, + { bondToBreak, { 2, 3 } }, + }; + + NvBlastChunkDesc chunkDescs[] = + { + { { 0, 0, 0 }, 2, UINT32_MAX, NvBlastChunkDesc::NoFlags, 'root' }, + { { -1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'A' }, + { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'B' }, + { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'C' }, + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 2; + assetDesc.bondDescs = bondDescs; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* rootActor = fwk->createActor(actorDesc); + rootActor->userData = (void*)'root'; + + class Listener : public TkEventListener + { + void receive(const TkEvent* events, uint32_t eventCount) override + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkJointUpdateEvent::EVENT_TYPE: + FAIL() << "not expecting joints here"; + break; + + case TkFractureCommands::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + + // Group::sync still needed the family for SharedMemory management. + EXPECT_TRUE(nullptr != actor.family); + + // original actor state is not preserved, the last test will fail + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + + // this information was invalid anyway + //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; + } + break; + + case TkFractureEvents::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + + // Group::sync still needed the family for SharedMemory management. + EXPECT_TRUE(nullptr != actor.family); + + // original actor state is not preserved, the last test will fail + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + + // this information was invalid anyway + //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; + } + break; + + case TkSplitEvent::EVENT_TYPE: + { + const TkSplitEvent* split = event.getPayload(); + EXPECT_EQ((void*)'root', split->parentData.userData); + EXPECT_EQ(0, split->parentData.index); + EXPECT_EQ(2, split->numChildren); + + uint32_t visibleChunkIndex[2]; + // child order is not mandatory + { + TkActor* a = split->children[1]; + EXPECT_EQ(2, a->getVisibleChunkCount()); // chunks A and B + a->getVisibleChunkIndices(visibleChunkIndex, 2); + uint32_t actorIndex = a->getIndex(); + EXPECT_EQ(0, actorIndex); // same index as the original actor + + // visible chunk order is not mandatory + EXPECT_EQ('B', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); + EXPECT_EQ('A', a->getAsset()->getChunks()[visibleChunkIndex[1]].userData); + } + + { + TkActor* a = split->children[0]; + EXPECT_EQ(1, a->getVisibleChunkCount()); + a->getVisibleChunkIndices(visibleChunkIndex, 1); + uint32_t actorIndex = a->getIndex(); + EXPECT_EQ(2, actorIndex); + EXPECT_EQ('C', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); + } + } + break; + + default: + FAIL() << "should not get here"; + } + } + } + } listener; + rootActor->getFamily().addListener(listener); + + // expected state for the original actor, see Listener + EXPECT_EQ((void*)'root', rootActor->userData); + EXPECT_EQ(0, rootActor->getIndex()); + EXPECT_EQ(1, rootActor->getVisibleChunkCount()); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*rootActor); + + // this will trigger one bond to break + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0, 0.5f, 0.5f); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + rootActor->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + releaseFramework(); +} + +TEST_F(TkTestStrict, SplitWarning) // GWD-167 +{ + createFramework(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + NvBlastChunkDesc chunkDescs[] = + { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' }, + { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'A' }, + { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'B' }, + { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'C' }, + { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'D' }, + { { -1,0,0 }, 1, 1, NvBlastChunkDesc::NoFlags, 'AAAA' }, + { { +1,0,0 }, 1, 2, NvBlastChunkDesc::NoFlags, 'BBBB' }, + { { -1,0,0 }, 1, 3, NvBlastChunkDesc::NoFlags, 'CCCC' }, + { { +1,0,0 }, 1, 4, NvBlastChunkDesc::NoFlags, 'DDDD' }, + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor = fwk->createActor(actorDesc); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + releaseFramework(); +} + + +TEST_F(TkTestAllowWarnings, ChangeThreadCountToZero) +{ + // tests that group still allocates memory for one worker + // by replacing to a 0 threads cpu dispatcher (warns) + // mainly relies on internal asserts + + class EventCounter : public TkEventListener + { + public: + EventCounter() :fracCommands(0), fracEvents(0) {} + + void receive(const TkEvent* events, uint32_t eventCount) + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkFractureCommands::EVENT_TYPE: + fracCommands++; + break; + case TkFractureEvents::EVENT_TYPE: + fracEvents++; + break; + default: + FAIL(); + // no split due to single chunk + // no joints + } + } + } + + uint32_t fracCommands, fracEvents; + } listener; + + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + NvBlastChunkDesc chunkDescs[] = { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor1 = fwk->createActor(actorDesc); + TkActor* actor2 = fwk->createActor(actorDesc); + TkActor* actor3 = fwk->createActor(actorDesc); + TkActor* actor4 = fwk->createActor(actorDesc); + + actor1->getFamily().addListener(listener); + actor2->getFamily().addListener(listener); + actor3->getFamily().addListener(listener); + actor4->getFamily().addListener(listener); + +#if USE_PHYSX_DISPATCHER + PxU32 affinity[] = { 1, 2, 4, 8 }; + PxDefaultCpuDispatcher* disp0 = PxDefaultCpuDispatcherCreate(0, affinity); + disp0->setRunProfiled(false); + PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); + disp4->setRunProfiled(false); +#else + TestCpuDispatcher* disp0 = new TestCpuDispatcher(0); + TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); +#endif + + m_taskman->setCpuDispatcher(*disp4); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor1); + group->addActor(*actor2); + m_taskman->setCpuDispatcher(*disp0); + //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); + group->addActor(*actor3); + group->addActor(*actor4); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor1->damage(getFalloffProgram(), &radialDamageParams); + actor2->damage(getFalloffProgram(), &radialDamageParams); + actor3->damage(getFalloffProgram(), &radialDamageParams); + actor4->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_EQ(4, listener.fracCommands); + EXPECT_EQ(4, listener.fracEvents); + + releaseFramework(); + + disp0->release(); + disp4->release(); +} + +TEST_F(TkTestStrict, ChangeThreadCountUp) +{ + // tests that group allocates more memory for additional workers + // by replacing to a higher thread count cpu dispatcher (warns) + // mainly relies on internal asserts + + class EventCounter : public TkEventListener + { + public: + EventCounter() :fracCommands(0), fracEvents(0) {} + + void receive(const TkEvent* events, uint32_t eventCount) + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkFractureCommands::EVENT_TYPE: + fracCommands++; + break; + case TkFractureEvents::EVENT_TYPE: + fracEvents++; + break; + default: + FAIL(); + // no split due to single chunk + // no joints + } + } + } + + uint32_t fracCommands, fracEvents; + } listener; + + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + NvBlastChunkDesc chunkDescs[] = { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor1 = fwk->createActor(actorDesc); + TkActor* actor2 = fwk->createActor(actorDesc); + TkActor* actor3 = fwk->createActor(actorDesc); + TkActor* actor4 = fwk->createActor(actorDesc); + + actor1->getFamily().addListener(listener); + actor2->getFamily().addListener(listener); + actor3->getFamily().addListener(listener); + actor4->getFamily().addListener(listener); + +#if USE_PHYSX_DISPATCHER + PxU32 affinity[] = { 1, 2, 4, 8 }; + PxDefaultCpuDispatcher* disp2 = PxDefaultCpuDispatcherCreate(2, affinity); + disp2->setRunProfiled(false); + PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); + disp4->setRunProfiled(false); +#else + TestCpuDispatcher* disp2 = new TestCpuDispatcher(2); + TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); +#endif + + m_taskman->setCpuDispatcher(*disp2); + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor1); + group->addActor(*actor2); + group->addActor(*actor3); + group->addActor(*actor4); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor1->damage(getFalloffProgram(), &radialDamageParams); + actor2->damage(getFalloffProgram(), &radialDamageParams); + actor3->damage(getFalloffProgram(), &radialDamageParams); + actor4->damage(getFalloffProgram(), &radialDamageParams); + + m_taskman->setCpuDispatcher(*disp4); + //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_EQ(4, listener.fracCommands); + EXPECT_EQ(4, listener.fracEvents); + + releaseFramework(); + + disp2->release(); + disp4->release(); +} + +TEST_F(TkTestAllowWarnings, GroupNoWorkers) +{ + // tests that group still works without a taskmanager + // a warnings is expected + // mainly relies on internal asserts + + class EventCounter : public TkEventListener + { + public: + EventCounter() :fracCommands(0), fracEvents(0) {} + + void receive(const TkEvent* events, uint32_t eventCount) + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkFractureCommands::EVENT_TYPE: + fracCommands++; + break; + case TkFractureEvents::EVENT_TYPE: + fracEvents++; + break; + default: + FAIL(); + // no split due to single chunk + // no joints + } + } + } + + uint32_t fracCommands, fracEvents; + } listener; + + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + NvBlastChunkDesc chunkDescs[] = { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor1 = fwk->createActor(actorDesc); + TkActor* actor2 = fwk->createActor(actorDesc); + TkActor* actor3 = fwk->createActor(actorDesc); + TkActor* actor4 = fwk->createActor(actorDesc); + + actor1->getFamily().addListener(listener); + actor2->getFamily().addListener(listener); + actor3->getFamily().addListener(listener); + actor4->getFamily().addListener(listener); + +#if USE_PHYSX_DISPATCHER + PxDefaultCpuDispatcher* disp = PxDefaultCpuDispatcherCreate(0); +#else + TestCpuDispatcher* disp = new TestCpuDispatcher(0); +#endif + m_taskman->setCpuDispatcher(*disp); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor1); + group->addActor(*actor2); + group->addActor(*actor3); + group->addActor(*actor4); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams programParams = { + &radialDamage, + getDefaultMaterial() + }; + + actor1->damage(getFalloffProgram(), &programParams); + actor2->damage(getFalloffProgram(), &programParams); + actor3->damage(getFalloffProgram(), &programParams); + actor4->damage(getFalloffProgram(), &programParams); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_EQ(4, listener.fracCommands); + EXPECT_EQ(4, listener.fracEvents); + + disp->release(); + + releaseFramework(); +} + diff --git a/test/src/utils/TaskDispatcher.h b/test/src/utils/TaskDispatcher.h old mode 100644 new mode 100755 index e44434f..09df3b4 --- a/test/src/utils/TaskDispatcher.h +++ b/test/src/utils/TaskDispatcher.h @@ -1,219 +1,219 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -class TaskDispatcher -{ -public: - class Task - { - public: - virtual void process() = 0; - virtual ~Task() {}; - }; - - typedef std::function)> OnTaskFinishedFunction; - - TaskDispatcher(uint32_t threadCount, OnTaskFinishedFunction onTaskFinished) : - m_workingThreadsCount(0), m_onTaskFinished(onTaskFinished) - { - m_threads.resize(threadCount); - for (uint32_t i = 0; i < threadCount; i++) - { - m_threads[i] = std::unique_ptr(new Thread(i, m_completionSemaphore)); - m_threads[i]->start(); - m_freeThreads.push(m_threads[i].get()); - } - } - - void addTask(std::unique_ptr task) - { - m_tasks.push(std::move(task)); - } - - void process() - { - // main loop - while (m_tasks.size() > 0 || m_workingThreadsCount > 0) - { - // assign tasks - while (!(m_tasks.empty() || m_freeThreads.empty())) - { - auto task = std::move(m_tasks.front()); - m_tasks.pop(); - - Thread* freeThread = m_freeThreads.front(); - m_freeThreads.pop(); - - freeThread->processTask(std::move(task)); - m_workingThreadsCount++; - } - - m_completionSemaphore.wait(); - - // check for completion - for (std::unique_ptr& thread : m_threads) - { - if (thread->isTaskFinished()) - { - std::unique_ptr task; - thread->collectTask(task); - m_onTaskFinished(*this, std::move(task)); - - m_freeThreads.push(thread.get()); - m_workingThreadsCount--; - break; - } - } - } - } - -private: - class Semaphore - { - public: - Semaphore(int count_ = 0) - : m_count(count_) {} - - inline void notify() - { - std::unique_lock lock(m_mutex); - m_count++; - m_cv.notify_one(); - } - - inline void wait() - { - std::unique_lock lock(m_mutex); - - while (m_count == 0){ - m_cv.wait(lock); - } - m_count--; - } - - - private: - std::mutex m_mutex; - std::condition_variable m_cv; - int m_count; - }; - - class Thread - { - public: - Thread(uint32_t id_, Semaphore& completionSemaphore) : m_id(id_), m_completionSemaphore(completionSemaphore), m_running(false), m_taskFinished(false) {} - virtual ~Thread() { stop(); } - - void start() - { - if (!m_running) - { - m_running = true; - m_thread = std::thread(&Thread::body, this); - } - } - - void stop() - { - if (m_running) - { - m_running = false; - m_newTaskSemaphore.notify(); - m_thread.join(); - } - } - - void processTask(std::unique_ptr task) - { - m_task = std::move(task); - m_taskFinished = false; - m_newTaskSemaphore.notify(); - } - - void collectTask(std::unique_ptr& task) - { - task = std::move(m_task); - m_task = nullptr; - m_taskFinished = false; - } - - bool hasTask() const { return m_task != nullptr; } - - bool isTaskFinished() const { return m_taskFinished; } - - private: - void body() - { - while (1) - { - m_newTaskSemaphore.wait(); - - if (!m_running) - return; - - m_task->process(); - m_taskFinished = true; - - m_completionSemaphore.notify(); - } - } - - uint32_t m_id; - Semaphore& m_completionSemaphore; - std::thread m_thread; - bool m_running; - - std::unique_ptr m_task; - std::atomic m_taskFinished; - - Semaphore m_newTaskSemaphore; - }; - -private: - uint32_t m_workingThreadsCount; - - std::queue> m_tasks; - OnTaskFinishedFunction m_onTaskFinished; - - std::vector> m_threads; - std::queue m_freeThreads; - - Semaphore m_completionSemaphore; -}; - +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +class TaskDispatcher +{ +public: + class Task + { + public: + virtual void process() = 0; + virtual ~Task() {}; + }; + + typedef std::function)> OnTaskFinishedFunction; + + TaskDispatcher(uint32_t threadCount, OnTaskFinishedFunction onTaskFinished) : + m_workingThreadsCount(0), m_onTaskFinished(onTaskFinished) + { + m_threads.resize(threadCount); + for (uint32_t i = 0; i < threadCount; i++) + { + m_threads[i] = std::unique_ptr(new Thread(i, m_completionSemaphore)); + m_threads[i]->start(); + m_freeThreads.push(m_threads[i].get()); + } + } + + void addTask(std::unique_ptr task) + { + m_tasks.push(std::move(task)); + } + + void process() + { + // main loop + while (m_tasks.size() > 0 || m_workingThreadsCount > 0) + { + // assign tasks + while (!(m_tasks.empty() || m_freeThreads.empty())) + { + auto task = std::move(m_tasks.front()); + m_tasks.pop(); + + Thread* freeThread = m_freeThreads.front(); + m_freeThreads.pop(); + + freeThread->processTask(std::move(task)); + m_workingThreadsCount++; + } + + m_completionSemaphore.wait(); + + // check for completion + for (std::unique_ptr& thread : m_threads) + { + if (thread->isTaskFinished()) + { + std::unique_ptr task; + thread->collectTask(task); + m_onTaskFinished(*this, std::move(task)); + + m_freeThreads.push(thread.get()); + m_workingThreadsCount--; + break; + } + } + } + } + +private: + class Semaphore + { + public: + Semaphore(int count_ = 0) + : m_count(count_) {} + + inline void notify() + { + std::unique_lock lock(m_mutex); + m_count++; + m_cv.notify_one(); + } + + inline void wait() + { + std::unique_lock lock(m_mutex); + + while (m_count == 0){ + m_cv.wait(lock); + } + m_count--; + } + + + private: + std::mutex m_mutex; + std::condition_variable m_cv; + int m_count; + }; + + class Thread + { + public: + Thread(uint32_t id_, Semaphore& completionSemaphore) : m_id(id_), m_completionSemaphore(completionSemaphore), m_running(false), m_taskFinished(false) {} + virtual ~Thread() { stop(); } + + void start() + { + if (!m_running) + { + m_running = true; + m_thread = std::thread(&Thread::body, this); + } + } + + void stop() + { + if (m_running) + { + m_running = false; + m_newTaskSemaphore.notify(); + m_thread.join(); + } + } + + void processTask(std::unique_ptr task) + { + m_task = std::move(task); + m_taskFinished = false; + m_newTaskSemaphore.notify(); + } + + void collectTask(std::unique_ptr& task) + { + task = std::move(m_task); + m_task = nullptr; + m_taskFinished = false; + } + + bool hasTask() const { return m_task != nullptr; } + + bool isTaskFinished() const { return m_taskFinished; } + + private: + void body() + { + while (1) + { + m_newTaskSemaphore.wait(); + + if (!m_running) + return; + + m_task->process(); + m_taskFinished = true; + + m_completionSemaphore.notify(); + } + } + + uint32_t m_id; + Semaphore& m_completionSemaphore; + std::thread m_thread; + bool m_running; + + std::unique_ptr m_task; + std::atomic m_taskFinished; + + Semaphore m_newTaskSemaphore; + }; + +private: + uint32_t m_workingThreadsCount; + + std::queue> m_tasks; + OnTaskFinishedFunction m_onTaskFinished; + + std::vector> m_threads; + std::queue m_freeThreads; + + Semaphore m_completionSemaphore; +}; + diff --git a/test/src/utils/TestAssets.cpp b/test/src/utils/TestAssets.cpp old mode 100644 new mode 100755 index dbf5627..88aece2 --- a/test/src/utils/TestAssets.cpp +++ b/test/src/utils/TestAssets.cpp @@ -1,420 +1,420 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "TestAssets.h" -#include "AssetGenerator.h" -#include - -const NvBlastChunkDesc g_cube1ChunkDescs[9] = -{ -// centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, - { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, -}; - -const NvBlastBondDesc g_cube1BondDescs[12] = -{ -// normal area centroid userData chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f,-0.5f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f,-0.5f }, 0 }, { 3, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f, 0.5f }, 0 }, { 5, 6 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f, 0.5f }, 0 }, { 7, 8 } }, - - { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f,-0.5f }, 0 }, { 1, 3 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f,-0.5f }, 0 }, { 2, 4 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f, 0.5f }, 0 }, { 5, 7 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f, 0.5f }, 0 }, { 6, 8 } }, - - { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f,-0.5f, 0.0f }, 0 }, { 1, 5 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f,-0.5f, 0.0f }, 0 }, { 2, 6 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f, 0.5f, 0.0f }, 0 }, { 3, 7 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f, 0.5f, 0.0f }, 0 }, { 4, 8 } }, -}; - -const NvBlastBondDesc g_cube1BondDescs_wb[16] = -{ -// normal area centroid userData chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f,-0.5f }, 0 }, { 1, 2 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f,-0.5f }, 0 }, { 3, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f, 0.5f }, 0 }, { 5, 6 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f, 0.5f }, 0 }, { 7, 8 } }, - - { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f,-0.5f }, 0 }, { 1, 3 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f,-0.5f }, 0 }, { 2, 4 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f, 0.5f }, 0 }, { 5, 7 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f, 0.5f }, 0 }, { 6, 8 } }, - - { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f,-0.5f, 0.0f }, 0 }, { 1, 5 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f,-0.5f, 0.0f }, 0 }, { 2, 6 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f, 0.5f, 0.0f }, 0 }, { 3, 7 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f, 0.5f, 0.0f }, 0 }, { 4, 8 } }, - - { { { 0.0f, 0.0f,-1.0f }, 1.0f, {-0.5f,-0.5f,-1.0f }, 0 }, { 1, UINT32_MAX } }, - { { { 0.0f, 0.0f,-1.0f }, 1.0f, { 0.5f,-0.5f,-1.0f }, 0 }, { 2, UINT32_MAX } }, - { { { 0.0f, 0.0f,-1.0f }, 1.0f, {-0.5f, 0.5f,-1.0f }, 0 }, { 3, UINT32_MAX } }, - { { { 0.0f, 0.0f,-1.0f }, 1.0f, { 0.5f, 0.5f,-1.0f }, 0 }, { 4, UINT32_MAX } }, -}; - -const NvBlastChunkDesc g_cube2ChunkDescs[73] = -{ -// centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {-0.5f, -0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.5f, -0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {-0.5f, 0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.5f, 0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {-0.5f, -0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { { 0.5f, -0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, - { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, - { {-0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 9 }, - { { 0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 10 }, - { {-0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 11 }, - { { 0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 12 }, - { {-0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 13 }, - { { 0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 14 }, - { {-0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 15 }, - { { 0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 16 }, - { {-0.25f+0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 17 }, - { { 0.25f+0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 18 }, - { {-0.25f+0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 19 }, - { { 0.25f+0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 20 }, - { {-0.25f+0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 21 }, - { { 0.25f+0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 22 }, - { {-0.25f+0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 23 }, - { { 0.25f+0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 24 }, - { {-0.25f-0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 25 }, - { { 0.25f-0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 26 }, - { {-0.25f-0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 27 }, - { { 0.25f-0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 28 }, - { {-0.25f-0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 29 }, - { { 0.25f-0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 30 }, - { {-0.25f-0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 31 }, - { { 0.25f-0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 32 }, - { {-0.25f+0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 33 }, - { { 0.25f+0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 34 }, - { {-0.25f+0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 35 }, - { { 0.25f+0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 36 }, - { {-0.25f+0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 37 }, - { { 0.25f+0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 38 }, - { {-0.25f+0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 39 }, - { { 0.25f+0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 40 }, - { {-0.25f-0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 41 }, - { { 0.25f-0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 42 }, - { {-0.25f-0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 43 }, - { { 0.25f-0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 44 }, - { {-0.25f-0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 45 }, - { { 0.25f-0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 46 }, - { {-0.25f-0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 47 }, - { { 0.25f-0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 48 }, - { {-0.25f+0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 49 }, - { { 0.25f+0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 50 }, - { {-0.25f+0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 51 }, - { { 0.25f+0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 52 }, - { {-0.25f+0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 53 }, - { { 0.25f+0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 54 }, - { {-0.25f+0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 55 }, - { { 0.25f+0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 56 }, - { {-0.25f-0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 57 }, - { { 0.25f-0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 58 }, - { {-0.25f-0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 59 }, - { { 0.25f-0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 60 }, - { {-0.25f-0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 61 }, - { { 0.25f-0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 62 }, - { {-0.25f-0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 63 }, - { { 0.25f-0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 64 }, - { {-0.25f+0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 65 }, - { { 0.25f+0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 66 }, - { {-0.25f+0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 67 }, - { { 0.25f+0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 68 }, - { {-0.25f+0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 69 }, - { { 0.25f+0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 70 }, - { {-0.25f+0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 71 }, - { { 0.25f+0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 72 }, -}; - -const NvBlastChunkDesc g_cube3ChunkDescs[11] = -{ -// centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 4.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { { 0.0f, 0.0f, 0.0f }, 3.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 1 }, - { { 0.0f, 0.0f, 0.0f }, 1.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 2 }, - { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { { 0.5f, 0.5f,-0.5f }, 1.0f, 1, NvBlastChunkDesc::SupportFlag, 6 }, - { {-0.5f,-0.5f, 0.5f }, 1.0f, 1, NvBlastChunkDesc::SupportFlag, 7 }, - { { 0.5f,-0.5f, 0.5f }, 1.0f, 1, NvBlastChunkDesc::SupportFlag, 8 }, - { {-0.5f, 0.5f, 0.5f }, 1.0f, 2, NvBlastChunkDesc::SupportFlag, 9 }, - { { 0.5f, 0.5f, 0.5f }, 1.0f, 2, NvBlastChunkDesc::SupportFlag, 10 }, -}; - -const NvBlastBondDesc g_cube3BondDescs[12] = -{ -// normal area centroid userData chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f,-0.5f }, 0 }, { 3, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f,-0.5f }, 0 }, { 5, 6 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f, 0.5f }, 0 }, { 7, 8 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f, 0.5f }, 0 }, { 9, 10} }, - - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f,-0.5f }, 0 }, { 3, 5 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f,-0.5f }, 0 }, { 4, 6 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f, 0.5f }, 0 }, { 7, 9 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f, 0.5f }, 0 }, { 8, 10} }, - - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f,-0.5f, 0.0f }, 0 }, { 3, 7 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f,-0.5f, 0.0f }, 0 }, { 4, 8 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f, 0.5f, 0.0f }, 0 }, { 5, 9 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f, 0.5f, 0.0f }, 0 }, { 6, 10} }, -}; - -const NvBlastBondDesc g_cube3BondDescs_wb[16] = -{ -// normal area centroid userData chunks - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f,-0.5f }, 0 }, { 3, 4 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f,-0.5f }, 0 }, { 5, 6 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f, 0.5f }, 0 }, { 7, 8 } }, - { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f, 0.5f }, 0 }, { 9, 10} }, - - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f,-0.5f }, 0 }, { 3, 5 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f,-0.5f }, 0 }, { 4, 6 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f, 0.5f }, 0 }, { 7, 9 } }, - { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f, 0.5f }, 0 }, { 8, 10} }, - - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f,-0.5f, 0.0f }, 0 }, { 3, 7 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f,-0.5f, 0.0f }, 0 }, { 4, 8 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f, 0.5f, 0.0f }, 0 }, { 5, 9 } }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f, 0.5f, 0.0f }, 0 }, { 6, 10} }, - - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f,-0.5f,-1.0f }, 0 }, { 3, UINT32_MAX} }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f,-0.5f,-1.0f }, 0 }, { 4, UINT32_MAX} }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f, 0.5f,-1.0f }, 0 }, { 5, UINT32_MAX} }, - { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f, 0.5f,-1.0f }, 0 }, { 6, UINT32_MAX} }, -}; - -const NvBlastAssetDesc g_assetDescs[6] = -{ - // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks - { sizeof(g_cube1ChunkDescs) / sizeof(g_cube1ChunkDescs[0]), g_cube1ChunkDescs, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, - - // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks which are then split into 8 depth-2 (1/2)x(1/2)x(1/2) child chunks each - // Support is at depth-1, so the g_cube1BondDescs are used - { sizeof(g_cube2ChunkDescs) / sizeof(g_cube2ChunkDescs[0]), g_cube2ChunkDescs, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, - - // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks with multiple roots - { sizeof(g_cube3ChunkDescs) / sizeof(g_cube3ChunkDescs[0]), g_cube3ChunkDescs, sizeof(g_cube3BondDescs) / sizeof(g_cube3BondDescs[0]), g_cube3BondDescs }, - - // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks - contains world-bound chunks - { sizeof(g_cube1ChunkDescs) / sizeof(g_cube1ChunkDescs[0]), g_cube1ChunkDescs, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, - - // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks which are then split into 8 depth-2 (1/2)x(1/2)x(1/2) child chunks each - contains world-bound chunks - // Support is at depth-1, so the g_cube1BondDescs_wb are used - { sizeof(g_cube2ChunkDescs) / sizeof(g_cube2ChunkDescs[0]), g_cube2ChunkDescs, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, - - // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks with multiple roots - contains world-bound chunks - { sizeof(g_cube3ChunkDescs) / sizeof(g_cube3ChunkDescs[0]), g_cube3ChunkDescs, sizeof(g_cube3BondDescs_wb) / sizeof(g_cube3BondDescs_wb[0]), g_cube3BondDescs_wb }, -}; - - - -struct ExpectedValues -{ - uint32_t totalChunkCount; - uint32_t graphNodeCount; - uint32_t leafChunkCount; - uint32_t bondCount; - uint32_t subsupportChunkCount; -}; - -const ExpectedAssetValues g_assetExpectedValues[6] = -{ -// total graph leaves bonds sub - { 9, 8, 8, 12, 0 }, - { 73, 8, 64, 12, 64 }, - { 11, 8, 8, 12, 0 }, - { 9, 9, 8, 16, 0 }, - { 73, 9, 64, 16, 64 }, - { 11, 9, 8, 16, 0 }, -}; - - -///////////// Badly-formed asset descs below ////////////// - -const NvBlastChunkDesc g_cube1ChunkDescsMissingCoverage[9] = -{ -// centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, - { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, - { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 8 }, -}; - - -const NvBlastChunkDesc g_cube2ChunkDescsMissingCoverage1[17] = -{ -// centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 1 }, - { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, - { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 8 }, - { {-0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 9 }, - { { 0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 10 }, - { {-0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 11 }, - { { 0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 12 }, - { {-0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 13 }, - { { 0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 14 }, - { {-0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 15 }, - { { 0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 16 }, -}; - - -const NvBlastChunkDesc g_cube2ChunkDescsMissingCoverage2[17] = -{ -// centroid volume parent idx flags ID - { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, - { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 1 }, - { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, - { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, - { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, - { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, - { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, - { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, - { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 8 }, - { {-0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 9 }, - { { 0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 10 }, - { {-0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 11 }, - { { 0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 12 }, - { {-0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 13 }, - { { 0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 14 }, - { {-0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 15 }, - { { 0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::SupportFlag, 16 }, -}; - - -const NvBlastAssetDesc g_assetDescsMissingCoverage[6] = -{ - { sizeof(g_cube1ChunkDescsMissingCoverage) / sizeof(g_cube1ChunkDescsMissingCoverage[0]), g_cube1ChunkDescsMissingCoverage, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, - { sizeof(g_cube2ChunkDescsMissingCoverage1) / sizeof(g_cube2ChunkDescsMissingCoverage1[0]), g_cube2ChunkDescsMissingCoverage1, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, - { sizeof(g_cube2ChunkDescsMissingCoverage2) / sizeof(g_cube2ChunkDescsMissingCoverage2[0]), g_cube2ChunkDescsMissingCoverage2, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, - { sizeof(g_cube1ChunkDescsMissingCoverage) / sizeof(g_cube1ChunkDescsMissingCoverage[0]), g_cube1ChunkDescsMissingCoverage, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, - { sizeof(g_cube2ChunkDescsMissingCoverage1) / sizeof(g_cube2ChunkDescsMissingCoverage1[0]), g_cube2ChunkDescsMissingCoverage1, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, - { sizeof(g_cube2ChunkDescsMissingCoverage2) / sizeof(g_cube2ChunkDescsMissingCoverage2[0]), g_cube2ChunkDescsMissingCoverage2, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, -}; - -extern const ExpectedAssetValues g_assetsFromMissingCoverageExpectedValues[6] = -{ -// total graph leaves bonds sub - { 9, 8, 8, 12, 0 }, - { 17, 8, 15, 12, 8 }, - { 17, 15, 15, 9, 0 }, - { 9, 9, 8, 16, 0 }, - { 17, 9, 15, 16, 8 }, - { 17, 16, 15, 12, 0 }, -}; - - -void generateCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& assetDesc, size_t maxDepth, size_t width, int32_t supportDepth, CubeAssetGenerator::BondFlags bondFlags) -{ - CubeAssetGenerator::Settings settings; - settings.extents = GeneratorAsset::Vec3(1, 1, 1); - CubeAssetGenerator::DepthInfo depthInfo; - depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); - depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; - settings.depths.push_back(depthInfo); - settings.bondFlags = bondFlags; - - for (uint32_t depth = 1; depth < maxDepth; ++depth) - { - depthInfo.slicesPerAxis = GeneratorAsset::Vec3((float)width, (float)width, (float)width); - settings.depths.push_back(depthInfo); - } - settings.depths[(supportDepth > 0 ? supportDepth : maxDepth) - 1].flag = NvBlastChunkDesc::SupportFlag; // Leaves are support - - CubeAssetGenerator::generate(cubeAsset, settings); - - assetDesc.bondCount = (uint32_t)cubeAsset.solverBonds.size(); - assetDesc.bondDescs = cubeAsset.solverBonds.data(); - assetDesc.chunkCount = (uint32_t)cubeAsset.chunks.size(); - assetDesc.chunkDescs = cubeAsset.solverChunks.data(); -} - -void generateRandomCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& desc, uint32_t minChunkCount, uint32_t maxChunkCount) -{ - CubeAssetGenerator::Settings settings; - settings.extents = GeneratorAsset::Vec3(1, 1, 1); - CubeAssetGenerator::DepthInfo depthInfo; - depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); - depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; - settings.depths.push_back(depthInfo); - uint32_t chunkCount = 1; - while (chunkCount < minChunkCount) - { - uint32_t chunkMul; - do - { - depthInfo.slicesPerAxis = GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); - chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); - } while (chunkMul == 1); - if (chunkCount*chunkMul > maxChunkCount) - { - break; - } - chunkCount *= chunkMul; - settings.depths.push_back(depthInfo); - settings.extents = settings.extents * depthInfo.slicesPerAxis; - } - settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support - - // Make largest direction unit size - settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); - - // Create asset - CubeAssetGenerator::generate(cubeAsset, settings); - - desc.chunkDescs = cubeAsset.solverChunks.data(); - desc.chunkCount = (uint32_t)cubeAsset.solverChunks.size(); - desc.bondDescs = cubeAsset.solverBonds.data(); - desc.bondCount = (uint32_t)cubeAsset.solverBonds.size(); +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "TestAssets.h" +#include "AssetGenerator.h" +#include + +const NvBlastChunkDesc g_cube1ChunkDescs[9] = +{ +// centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, + { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, +}; + +const NvBlastBondDesc g_cube1BondDescs[12] = +{ +// normal area centroid userData chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f,-0.5f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f,-0.5f }, 0 }, { 3, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f, 0.5f }, 0 }, { 5, 6 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f, 0.5f }, 0 }, { 7, 8 } }, + + { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f,-0.5f }, 0 }, { 1, 3 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f,-0.5f }, 0 }, { 2, 4 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f, 0.5f }, 0 }, { 5, 7 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f, 0.5f }, 0 }, { 6, 8 } }, + + { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f,-0.5f, 0.0f }, 0 }, { 1, 5 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f,-0.5f, 0.0f }, 0 }, { 2, 6 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f, 0.5f, 0.0f }, 0 }, { 3, 7 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f, 0.5f, 0.0f }, 0 }, { 4, 8 } }, +}; + +const NvBlastBondDesc g_cube1BondDescs_wb[16] = +{ +// normal area centroid userData chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f,-0.5f }, 0 }, { 1, 2 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f,-0.5f }, 0 }, { 3, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f,-0.5f, 0.5f }, 0 }, { 5, 6 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, 0.5f, 0.5f }, 0 }, { 7, 8 } }, + + { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f,-0.5f }, 0 }, { 1, 3 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f,-0.5f }, 0 }, { 2, 4 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, {-0.5f, 0.0f, 0.5f }, 0 }, { 5, 7 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f, { 0.5f, 0.0f, 0.5f }, 0 }, { 6, 8 } }, + + { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f,-0.5f, 0.0f }, 0 }, { 1, 5 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f,-0.5f, 0.0f }, 0 }, { 2, 6 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f, {-0.5f, 0.5f, 0.0f }, 0 }, { 3, 7 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f, { 0.5f, 0.5f, 0.0f }, 0 }, { 4, 8 } }, + + { { { 0.0f, 0.0f,-1.0f }, 1.0f, {-0.5f,-0.5f,-1.0f }, 0 }, { 1, UINT32_MAX } }, + { { { 0.0f, 0.0f,-1.0f }, 1.0f, { 0.5f,-0.5f,-1.0f }, 0 }, { 2, UINT32_MAX } }, + { { { 0.0f, 0.0f,-1.0f }, 1.0f, {-0.5f, 0.5f,-1.0f }, 0 }, { 3, UINT32_MAX } }, + { { { 0.0f, 0.0f,-1.0f }, 1.0f, { 0.5f, 0.5f,-1.0f }, 0 }, { 4, UINT32_MAX } }, +}; + +const NvBlastChunkDesc g_cube2ChunkDescs[73] = +{ +// centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {-0.5f, -0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.5f, -0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {-0.5f, 0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.5f, 0.5f, -0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {-0.5f, -0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { { 0.5f, -0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, + { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 8 }, + { {-0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 9 }, + { { 0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 10 }, + { {-0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 11 }, + { { 0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 12 }, + { {-0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 13 }, + { { 0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 14 }, + { {-0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 15 }, + { { 0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 16 }, + { {-0.25f+0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 17 }, + { { 0.25f+0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 18 }, + { {-0.25f+0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 19 }, + { { 0.25f+0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 20 }, + { {-0.25f+0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 21 }, + { { 0.25f+0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 22 }, + { {-0.25f+0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 23 }, + { { 0.25f+0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 2, NvBlastChunkDesc::NoFlags, 24 }, + { {-0.25f-0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 25 }, + { { 0.25f-0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 26 }, + { {-0.25f-0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 27 }, + { { 0.25f-0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 28 }, + { {-0.25f-0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 29 }, + { { 0.25f-0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 30 }, + { {-0.25f-0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 31 }, + { { 0.25f-0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 3, NvBlastChunkDesc::NoFlags, 32 }, + { {-0.25f+0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 33 }, + { { 0.25f+0.5f,-0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 34 }, + { {-0.25f+0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 35 }, + { { 0.25f+0.5f, 0.25f+0.5f,-0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 36 }, + { {-0.25f+0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 37 }, + { { 0.25f+0.5f,-0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 38 }, + { {-0.25f+0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 39 }, + { { 0.25f+0.5f, 0.25f+0.5f, 0.25f-0.5f }, 0.125f, 4, NvBlastChunkDesc::NoFlags, 40 }, + { {-0.25f-0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 41 }, + { { 0.25f-0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 42 }, + { {-0.25f-0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 43 }, + { { 0.25f-0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 44 }, + { {-0.25f-0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 45 }, + { { 0.25f-0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 46 }, + { {-0.25f-0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 47 }, + { { 0.25f-0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 5, NvBlastChunkDesc::NoFlags, 48 }, + { {-0.25f+0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 49 }, + { { 0.25f+0.5f,-0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 50 }, + { {-0.25f+0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 51 }, + { { 0.25f+0.5f, 0.25f-0.5f,-0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 52 }, + { {-0.25f+0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 53 }, + { { 0.25f+0.5f,-0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 54 }, + { {-0.25f+0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 55 }, + { { 0.25f+0.5f, 0.25f-0.5f, 0.25f+0.5f }, 0.125f, 6, NvBlastChunkDesc::NoFlags, 56 }, + { {-0.25f-0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 57 }, + { { 0.25f-0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 58 }, + { {-0.25f-0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 59 }, + { { 0.25f-0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 60 }, + { {-0.25f-0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 61 }, + { { 0.25f-0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 62 }, + { {-0.25f-0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 63 }, + { { 0.25f-0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 7, NvBlastChunkDesc::NoFlags, 64 }, + { {-0.25f+0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 65 }, + { { 0.25f+0.5f,-0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 66 }, + { {-0.25f+0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 67 }, + { { 0.25f+0.5f, 0.25f+0.5f,-0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 68 }, + { {-0.25f+0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 69 }, + { { 0.25f+0.5f,-0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 70 }, + { {-0.25f+0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 71 }, + { { 0.25f+0.5f, 0.25f+0.5f, 0.25f+0.5f }, 0.125f, 8, NvBlastChunkDesc::NoFlags, 72 }, +}; + +const NvBlastChunkDesc g_cube3ChunkDescs[11] = +{ +// centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 4.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { { 0.0f, 0.0f, 0.0f }, 3.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 1 }, + { { 0.0f, 0.0f, 0.0f }, 1.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 2 }, + { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { { 0.5f, 0.5f,-0.5f }, 1.0f, 1, NvBlastChunkDesc::SupportFlag, 6 }, + { {-0.5f,-0.5f, 0.5f }, 1.0f, 1, NvBlastChunkDesc::SupportFlag, 7 }, + { { 0.5f,-0.5f, 0.5f }, 1.0f, 1, NvBlastChunkDesc::SupportFlag, 8 }, + { {-0.5f, 0.5f, 0.5f }, 1.0f, 2, NvBlastChunkDesc::SupportFlag, 9 }, + { { 0.5f, 0.5f, 0.5f }, 1.0f, 2, NvBlastChunkDesc::SupportFlag, 10 }, +}; + +const NvBlastBondDesc g_cube3BondDescs[12] = +{ +// normal area centroid userData chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f,-0.5f }, 0 }, { 3, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f,-0.5f }, 0 }, { 5, 6 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f, 0.5f }, 0 }, { 7, 8 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f, 0.5f }, 0 }, { 9, 10} }, + + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f,-0.5f }, 0 }, { 3, 5 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f,-0.5f }, 0 }, { 4, 6 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f, 0.5f }, 0 }, { 7, 9 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f, 0.5f }, 0 }, { 8, 10} }, + + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f,-0.5f, 0.0f }, 0 }, { 3, 7 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f,-0.5f, 0.0f }, 0 }, { 4, 8 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f, 0.5f, 0.0f }, 0 }, { 5, 9 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f, 0.5f, 0.0f }, 0 }, { 6, 10} }, +}; + +const NvBlastBondDesc g_cube3BondDescs_wb[16] = +{ +// normal area centroid userData chunks + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f,-0.5f }, 0 }, { 3, 4 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f,-0.5f }, 0 }, { 5, 6 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f,-0.5f, 0.5f }, 0 }, { 7, 8 } }, + { { { 1.0f, 0.0f, 0.0f }, 1.0f,{ 0.0f, 0.5f, 0.5f }, 0 }, { 9, 10} }, + + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f,-0.5f }, 0 }, { 3, 5 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f,-0.5f }, 0 }, { 4, 6 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{-0.5f, 0.0f, 0.5f }, 0 }, { 7, 9 } }, + { { { 0.0f, 1.0f, 0.0f }, 1.0f,{ 0.5f, 0.0f, 0.5f }, 0 }, { 8, 10} }, + + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f,-0.5f, 0.0f }, 0 }, { 3, 7 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f,-0.5f, 0.0f }, 0 }, { 4, 8 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f, 0.5f, 0.0f }, 0 }, { 5, 9 } }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f, 0.5f, 0.0f }, 0 }, { 6, 10} }, + + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f,-0.5f,-1.0f }, 0 }, { 3, UINT32_MAX} }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f,-0.5f,-1.0f }, 0 }, { 4, UINT32_MAX} }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{-0.5f, 0.5f,-1.0f }, 0 }, { 5, UINT32_MAX} }, + { { { 0.0f, 0.0f, 1.0f }, 1.0f,{ 0.5f, 0.5f,-1.0f }, 0 }, { 6, UINT32_MAX} }, +}; + +const NvBlastAssetDesc g_assetDescs[6] = +{ + // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks + { sizeof(g_cube1ChunkDescs) / sizeof(g_cube1ChunkDescs[0]), g_cube1ChunkDescs, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, + + // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks which are then split into 8 depth-2 (1/2)x(1/2)x(1/2) child chunks each + // Support is at depth-1, so the g_cube1BondDescs are used + { sizeof(g_cube2ChunkDescs) / sizeof(g_cube2ChunkDescs[0]), g_cube2ChunkDescs, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, + + // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks with multiple roots + { sizeof(g_cube3ChunkDescs) / sizeof(g_cube3ChunkDescs[0]), g_cube3ChunkDescs, sizeof(g_cube3BondDescs) / sizeof(g_cube3BondDescs[0]), g_cube3BondDescs }, + + // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks - contains world-bound chunks + { sizeof(g_cube1ChunkDescs) / sizeof(g_cube1ChunkDescs[0]), g_cube1ChunkDescs, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, + + // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks which are then split into 8 depth-2 (1/2)x(1/2)x(1/2) child chunks each - contains world-bound chunks + // Support is at depth-1, so the g_cube1BondDescs_wb are used + { sizeof(g_cube2ChunkDescs) / sizeof(g_cube2ChunkDescs[0]), g_cube2ChunkDescs, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, + + // 2x2x2 axis-aligned cube centered at the origin, split into 8 depth-1 1x1x1 child chunks with multiple roots - contains world-bound chunks + { sizeof(g_cube3ChunkDescs) / sizeof(g_cube3ChunkDescs[0]), g_cube3ChunkDescs, sizeof(g_cube3BondDescs_wb) / sizeof(g_cube3BondDescs_wb[0]), g_cube3BondDescs_wb }, +}; + + + +struct ExpectedValues +{ + uint32_t totalChunkCount; + uint32_t graphNodeCount; + uint32_t leafChunkCount; + uint32_t bondCount; + uint32_t subsupportChunkCount; +}; + +const ExpectedAssetValues g_assetExpectedValues[6] = +{ +// total graph leaves bonds sub + { 9, 8, 8, 12, 0 }, + { 73, 8, 64, 12, 64 }, + { 11, 8, 8, 12, 0 }, + { 9, 9, 8, 16, 0 }, + { 73, 9, 64, 16, 64 }, + { 11, 9, 8, 16, 0 }, +}; + + +///////////// Badly-formed asset descs below ////////////// + +const NvBlastChunkDesc g_cube1ChunkDescsMissingCoverage[9] = +{ +// centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, + { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 8 }, +}; + + +const NvBlastChunkDesc g_cube2ChunkDescsMissingCoverage1[17] = +{ +// centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 1 }, + { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, + { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 8 }, + { {-0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 9 }, + { { 0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 10 }, + { {-0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 11 }, + { { 0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 12 }, + { {-0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 13 }, + { { 0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 14 }, + { {-0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 15 }, + { { 0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 16 }, +}; + + +const NvBlastChunkDesc g_cube2ChunkDescsMissingCoverage2[17] = +{ +// centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 8.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {-0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 1 }, + { { 0.5f,-0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {-0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.5f, 0.5f,-0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {-0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { { 0.5f,-0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {-0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::SupportFlag, 7 }, + { { 0.5f, 0.5f, 0.5f }, 1.0f, 0, NvBlastChunkDesc::NoFlags, 8 }, + { {-0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 9 }, + { { 0.25f-0.5f,-0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 10 }, + { {-0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 11 }, + { { 0.25f-0.5f, 0.25f-0.5f,-0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 12 }, + { {-0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 13 }, + { { 0.25f-0.5f,-0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 14 }, + { {-0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::NoFlags, 15 }, + { { 0.25f-0.5f, 0.25f-0.5f, 0.25f-0.5f }, 0.125f, 1, NvBlastChunkDesc::SupportFlag, 16 }, +}; + + +const NvBlastAssetDesc g_assetDescsMissingCoverage[6] = +{ + { sizeof(g_cube1ChunkDescsMissingCoverage) / sizeof(g_cube1ChunkDescsMissingCoverage[0]), g_cube1ChunkDescsMissingCoverage, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, + { sizeof(g_cube2ChunkDescsMissingCoverage1) / sizeof(g_cube2ChunkDescsMissingCoverage1[0]), g_cube2ChunkDescsMissingCoverage1, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, + { sizeof(g_cube2ChunkDescsMissingCoverage2) / sizeof(g_cube2ChunkDescsMissingCoverage2[0]), g_cube2ChunkDescsMissingCoverage2, sizeof(g_cube1BondDescs) / sizeof(g_cube1BondDescs[0]), g_cube1BondDescs }, + { sizeof(g_cube1ChunkDescsMissingCoverage) / sizeof(g_cube1ChunkDescsMissingCoverage[0]), g_cube1ChunkDescsMissingCoverage, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, + { sizeof(g_cube2ChunkDescsMissingCoverage1) / sizeof(g_cube2ChunkDescsMissingCoverage1[0]), g_cube2ChunkDescsMissingCoverage1, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, + { sizeof(g_cube2ChunkDescsMissingCoverage2) / sizeof(g_cube2ChunkDescsMissingCoverage2[0]), g_cube2ChunkDescsMissingCoverage2, sizeof(g_cube1BondDescs_wb) / sizeof(g_cube1BondDescs_wb[0]), g_cube1BondDescs_wb }, +}; + +extern const ExpectedAssetValues g_assetsFromMissingCoverageExpectedValues[6] = +{ +// total graph leaves bonds sub + { 9, 8, 8, 12, 0 }, + { 17, 8, 15, 12, 8 }, + { 17, 15, 15, 9, 0 }, + { 9, 9, 8, 16, 0 }, + { 17, 9, 15, 16, 8 }, + { 17, 16, 15, 12, 0 }, +}; + + +void generateCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& assetDesc, size_t maxDepth, size_t width, int32_t supportDepth, CubeAssetGenerator::BondFlags bondFlags) +{ + CubeAssetGenerator::Settings settings; + settings.extents = GeneratorAsset::Vec3(1, 1, 1); + CubeAssetGenerator::DepthInfo depthInfo; + depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); + depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; + settings.depths.push_back(depthInfo); + settings.bondFlags = bondFlags; + + for (uint32_t depth = 1; depth < maxDepth; ++depth) + { + depthInfo.slicesPerAxis = GeneratorAsset::Vec3((float)width, (float)width, (float)width); + settings.depths.push_back(depthInfo); + } + settings.depths[(supportDepth > 0 ? supportDepth : maxDepth) - 1].flag = NvBlastChunkDesc::SupportFlag; // Leaves are support + + CubeAssetGenerator::generate(cubeAsset, settings); + + assetDesc.bondCount = (uint32_t)cubeAsset.solverBonds.size(); + assetDesc.bondDescs = cubeAsset.solverBonds.data(); + assetDesc.chunkCount = (uint32_t)cubeAsset.chunks.size(); + assetDesc.chunkDescs = cubeAsset.solverChunks.data(); +} + +void generateRandomCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& desc, uint32_t minChunkCount, uint32_t maxChunkCount) +{ + CubeAssetGenerator::Settings settings; + settings.extents = GeneratorAsset::Vec3(1, 1, 1); + CubeAssetGenerator::DepthInfo depthInfo; + depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); + depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; + settings.depths.push_back(depthInfo); + uint32_t chunkCount = 1; + while (chunkCount < minChunkCount) + { + uint32_t chunkMul; + do + { + depthInfo.slicesPerAxis = GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); + chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); + } while (chunkMul == 1); + if (chunkCount*chunkMul > maxChunkCount) + { + break; + } + chunkCount *= chunkMul; + settings.depths.push_back(depthInfo); + settings.extents = settings.extents * depthInfo.slicesPerAxis; + } + settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support + + // Make largest direction unit size + settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); + + // Create asset + CubeAssetGenerator::generate(cubeAsset, settings); + + desc.chunkDescs = cubeAsset.solverChunks.data(); + desc.chunkCount = (uint32_t)cubeAsset.solverChunks.size(); + desc.bondDescs = cubeAsset.solverBonds.data(); + desc.bondCount = (uint32_t)cubeAsset.solverBonds.size(); } \ No newline at end of file diff --git a/test/src/utils/TestAssets.h b/test/src/utils/TestAssets.h old mode 100644 new mode 100755 index a4d7e6b..cc3dc36 --- a/test/src/utils/TestAssets.h +++ b/test/src/utils/TestAssets.h @@ -1,70 +1,70 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#ifndef TESTASSETS_H -#define TESTASSETS_H - -#include "NvBlast.h" -#include "AssetGenerator.h" - -struct ExpectedAssetValues -{ - uint32_t totalChunkCount; - uint32_t graphNodeCount; - uint32_t leafChunkCount; - uint32_t bondCount; - uint32_t subsupportChunkCount; -}; - - -// Indexable asset descriptors and expected values -extern const NvBlastAssetDesc g_assetDescs[6]; -extern const ExpectedAssetValues g_assetExpectedValues[6]; - -// Indexable asset descriptors for assets missing coverage and expected values -extern const NvBlastAssetDesc g_assetDescsMissingCoverage[6]; -extern const ExpectedAssetValues g_assetsFromMissingCoverageExpectedValues[6]; - - -inline uint32_t getAssetDescCount() -{ - return sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); -} - -inline uint32_t getAssetDescMissingCoverageCount() -{ - return sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); -} - - -void generateCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& assetDesc, size_t maxDepth, size_t width, - int32_t supportDepth = -1, CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::ALL_INTERNAL_BONDS); - -void generateRandomCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& assetDesc, uint32_t minChunkCount, uint32_t maxChunkCount); - -#endif // #ifdef TESTASSETS_H +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef TESTASSETS_H +#define TESTASSETS_H + +#include "NvBlast.h" +#include "AssetGenerator.h" + +struct ExpectedAssetValues +{ + uint32_t totalChunkCount; + uint32_t graphNodeCount; + uint32_t leafChunkCount; + uint32_t bondCount; + uint32_t subsupportChunkCount; +}; + + +// Indexable asset descriptors and expected values +extern const NvBlastAssetDesc g_assetDescs[6]; +extern const ExpectedAssetValues g_assetExpectedValues[6]; + +// Indexable asset descriptors for assets missing coverage and expected values +extern const NvBlastAssetDesc g_assetDescsMissingCoverage[6]; +extern const ExpectedAssetValues g_assetsFromMissingCoverageExpectedValues[6]; + + +inline uint32_t getAssetDescCount() +{ + return sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); +} + +inline uint32_t getAssetDescMissingCoverageCount() +{ + return sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); +} + + +void generateCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& assetDesc, size_t maxDepth, size_t width, + int32_t supportDepth = -1, CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::ALL_INTERNAL_BONDS); + +void generateRandomCube(GeneratorAsset& cubeAsset, NvBlastAssetDesc& assetDesc, uint32_t minChunkCount, uint32_t maxChunkCount); + +#endif // #ifdef TESTASSETS_H diff --git a/test/src/utils/TestProfiler.h b/test/src/utils/TestProfiler.h old mode 100644 new mode 100755 index 7d80d88..5fc3bb4 --- a/test/src/utils/TestProfiler.h +++ b/test/src/utils/TestProfiler.h @@ -1,35 +1,35 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and 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. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#ifndef TESTPROFILER_H -#define TESTPROFILER_H - -#define TEST_ZONE_BEGIN(name) platformZoneStart(name) -#define TEST_ZONE_END(name) platformZoneEnd() - -#endif // TESTPROFILER_H +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef TESTPROFILER_H +#define TESTPROFILER_H + +#define TEST_ZONE_BEGIN(name) platformZoneStart(name) +#define TEST_ZONE_END(name) platformZoneEnd() + +#endif // TESTPROFILER_H -- cgit v1.2.3