diff options
Diffstat (limited to 'test/src/unit')
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/APITests.cpp | 3446 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/ActorTests.cpp | 2236 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/AssetTests.cpp | 1716 | ||||
| -rw-r--r-- | test/src/unit/AuthoringCutout.cpp | 410 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/CoreTests.cpp | 866 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/FamilyGraphTests.cpp | 810 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/MultithreadingTests.cpp | 840 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/SyncTests.cpp | 688 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/TkCompositeTests.cpp | 1550 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/src/unit/TkTests.cpp | 3214 |
10 files changed, 7683 insertions, 8093 deletions
diff --git a/test/src/unit/APITests.cpp b/test/src/unit/APITests.cpp index 655a65c..d1ff4cd 100644..100755 --- 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 <algorithm> - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// 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<char> 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<char> 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<uint32_t> 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<char> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> 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<uint32_t>(chunkFractureData.capacity()), nullptr, chunkFractureData.data() }; - { - NvBlastFractureBuffers events = target; - NvBlastFractureBuffers commands = { 0, static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> cfData; - cfData.resize(0 + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector<NvBlastBondFractureData> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> cfData; - cfData.resize(i + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector<NvBlastBondFractureData> bfData; - - NvBlastChunkFractureData command[] = - { - { 0, 1, 10.0f }, - { 0, 2, 10.0f }, - }; - - NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; - NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastChunkFractureData> cfData; - cfData.resize(14 + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector<NvBlastBondFractureData> bfData; - - NvBlastChunkFractureData command[] = - { - { 0, 1, 10.0f }, - { 0, 2, 10.0f }, - }; - - NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; - NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> cfData; - cfData.resize(2 + i + 1); - - std::vector<NvBlastBondFractureData> 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<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastChunkFractureData> cfData; - cfData.resize(14 + 1); - cfData[cfData.size() - 1].userdata = GUARD; - std::vector<NvBlastBondFractureData> 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<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastActor*> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<uint32_t> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); - - EXPECT_EQ(0, newActorsCount); - EXPECT_EQ(nullptr, result.deletedActor); - } - } - - // damage bonds, split actors in 2 each - std::vector<NvBlastActor*> actors2; - { - for (uint32_t i = 0; i < 2; ++i) - { - NvBlastActor* actor = actors[i]; - - // get graph nodes check - std::vector<uint32_t> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastActor*> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(desc)); - std::vector<uint32_t> chunkMap(desc->chunkCount); - NvBlastAsset* asset = NvBlastCreateAsset(&chunkMap[0], desc, alignedAlloc<malloc>, scratch.data(), nullptr); - EXPECT_TRUE(asset); - - // Test map - Nv::Blast::Asset& a = static_cast<Nv::Blast::Asset&>(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<char> 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<char> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); - NvBlastActorSplitEvent result; - result.deletedActor = nullptr; - result.newActors = newActors.data(); - size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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 <windows.h> -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::vector<char>scratch(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::vector<char>scratch(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 <algorithm>
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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<char> 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<char> 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<uint32_t> 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<char> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> 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<uint32_t>(chunkFractureData.capacity()), nullptr, chunkFractureData.data() };
+ {
+ NvBlastFractureBuffers events = target;
+ NvBlastFractureBuffers commands = { 0, static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> cfData;
+ cfData.resize(0 + 1);
+ cfData[cfData.size() - 1].userdata = GUARD;
+ std::vector<NvBlastBondFractureData> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> cfData;
+ cfData.resize(i + 1);
+ cfData[cfData.size() - 1].userdata = GUARD;
+ std::vector<NvBlastBondFractureData> bfData;
+
+ NvBlastChunkFractureData command[] =
+ {
+ { 0, 1, 10.0f },
+ { 0, 2, 10.0f },
+ };
+
+ NvBlastFractureBuffers commands = { 0, 2, nullptr, command };
+ NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastChunkFractureData> cfData;
+ cfData.resize(14 + 1);
+ cfData[cfData.size() - 1].userdata = GUARD;
+ std::vector<NvBlastBondFractureData> bfData;
+
+ NvBlastChunkFractureData command[] =
+ {
+ { 0, 1, 10.0f },
+ { 0, 2, 10.0f },
+ };
+
+ NvBlastFractureBuffers commands = { 0, 2, nullptr, command };
+ NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastChunkFractureData> cfData;
+ cfData.resize(2 + i + 1);
+
+ std::vector<NvBlastBondFractureData> 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<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastChunkFractureData> cfData;
+ cfData.resize(14 + 1);
+ cfData[cfData.size() - 1].userdata = GUARD;
+ std::vector<NvBlastBondFractureData> 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<uint32_t>(bfData.size()), static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastActor*> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<uint32_t> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr);
+
+ EXPECT_EQ(0, newActorsCount);
+ EXPECT_EQ(nullptr, result.deletedActor);
+ }
+ }
+
+ // damage bonds, split actors in 2 each
+ std::vector<NvBlastActor*> actors2;
+ {
+ for (uint32_t i = 0; i < 2; ++i)
+ {
+ NvBlastActor* actor = actors[i];
+
+ // get graph nodes check
+ std::vector<uint32_t> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> 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<NvBlastActor*> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(desc));
+ std::vector<uint32_t> chunkMap(desc->chunkCount);
+ NvBlastAsset* asset = NvBlastCreateAsset(&chunkMap[0], desc, alignedAlloc<malloc>, scratch.data(), nullptr);
+ EXPECT_TRUE(asset);
+
+ // Test map
+ Nv::Blast::Asset& a = static_cast<Nv::Blast::Asset&>(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<char> 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<char> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> 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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog));
+ NvBlastActorSplitEvent result;
+ result.deletedActor = nullptr;
+ result.newActors = newActors.data();
+ size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(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 <windows.h>
+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::vector<char>scratch(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::vector<char>scratch(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 index d4de648..79bb1fc 100644..100755 --- 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 <map> -#include <algorithm> - -#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<uint32_t> 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<uint32_t> 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<NvBlastActor*>& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) -{ - std::vector<NvBlastChunkFractureData> chunkEvents; /* num lower-support chunks + bonds */ - std::vector<NvBlastBondFractureData> bondEvents; /* num lower-support chunks + bonds */ - chunkEvents.resize(testAsset->solverChunks.size()); - bondEvents.resize(testAsset->solverBonds.size()); - - - std::vector<char> splitScratch; - std::vector<NvBlastActor*> 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<NvBlastActor*>::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) - { - NvBlastActor* actor = *k; - NvBlastFractureBuffers events = { static_cast<uint32_t>(bondEvents.size()), static_cast<uint32_t>(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<int FailLevel, int Verbosity> -class ActorTest : public BlastBaseTest<FailLevel, Verbosity> -{ -public: - ActorTest() - { - - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - BlastBaseTest<FailLevel, Verbosity>::alignedFree(mem); - } - - NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc) - { - // fix desc if wrong order or missing coverage first - NvBlastAssetDesc fixedDesc = desc; - std::vector<NvBlastChunkDesc> chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount); - std::vector<NvBlastBondDesc> bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount); - std::vector<uint32_t> chunkReorderMap(desc.chunkCount); - std::vector<char> 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<char> 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<Nv::Blast::Actor*>&, uint32_t, uint32_t, bool) - ) - { - const Nv::Blast::Asset& solverAsset = *static_cast<const Nv::Blast::Asset*>(&asset); - - std::vector<Nv::Blast::Actor*> actors; - std::vector<Nv::Blast::Actor*> buffer(NvBlastAssetGetChunkCount(&asset, messageLog)); - - // Instance the first actor from the asset - actors.push_back(static_cast<Nv::Blast::Actor*>(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<uint32_t>()) - { - 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<Nv::Blast::Actor*>& 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<bool> 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<uint32_t>()) - { - if (isSupport[chunkIndex]) - { - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - - EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); - } - else - { - // Array of visibility flags - std::vector<bool> 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<bool> 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<uint32_t>()) - { - // 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<Nv::Blast::Actor*>& 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<Nv::Blast::Actor*>&, 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<const char*>(family1); - const char* block2 = reinterpret_cast<const char*>(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<NvBlastActor*>& 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<NvBlastActor*>& actors, NvBlastLog logFn) - { - if (actors.size()) - { - EXPECT_LT(s_curr, s_storage.size()); - const NvBlastFamily* family = reinterpret_cast<NvBlastFamily*>(&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<Nv::Blast::Actor*>(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - std::vector<char> storageFamilyCopy((char*)family, (char*)family + size); - NvBlastFamily* storageFamily = reinterpret_cast<NvBlastFamily*>(storageFamilyCopy.data()); - NvBlastFamilySetAsset(storageFamily, solverAsset, logFn); - { - const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn); - std::vector<NvBlastActor*> 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<NvBlastActor*>& actors, NvBlastLog logFn) - { - if (actors.size() == 0) - { - return; - } - - Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - - const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); - - std::vector< std::vector<char> > 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<char>& 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<size_t> 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<NvBlastActor*>& actors, NvBlastLog logFn) - { - if (actors.size() <= 1) - { - return; - } - - Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - - const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); - const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn); - std::vector<char> buffer((char*)oldFamily, (char*)oldFamily + size); - NvBlastFamily* familyCopy = reinterpret_cast<NvBlastFamily*>(buffer.data()); - - const uint32_t serCount = 1 + (rand() % actors.size() - 1); - - const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn); - std::vector<NvBlastActor*> 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<char> > streams(serCount); - for (uint32_t i = 0; i < serCount; ++i) - { - std::vector<char>& 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<NvBlastActor*>&, 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<NvBlastActor*> 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<NvBlastActor*> buffer1(actorCount); - const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog); - EXPECT_EQ(actorsWritten, actorCount); - std::vector<NvBlastActor*> 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<NvBlastActor*>::iterator k = actors.begin(); k != actors.end(); ++k) - { - actorTest(*static_cast<Nv::Blast::Actor*>(*k), messageLog); - } - } - } - if (printActorCount) std::cout << "\n"; - - // Test fractured actor set - if (postDamageTest) - { - std::vector<NvBlastActor*> actorArray(actors.begin(), actors.end()); - postDamageTest(actorArray, messageLog); - } - - // Release remaining actors - for (std::set<NvBlastActor*>::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<NvBlastAsset*> m_assets; - std::vector<NvBlastActor*> m_actors; - std::vector<char> m_scratch; - static std::vector<char> s_storage; - - static size_t s_curr; -}; - -// Static values -template<int FailLevel, int Verbosity> -std::vector<char> ActorTest<FailLevel, Verbosity>::s_storage; - -template<int FailLevel, int Verbosity> -size_t ActorTest<FailLevel, Verbosity>::s_curr; - -// Specializations -typedef ActorTest<NvBlastMessage::Error, 1> ActorTestAllowWarnings; -typedef ActorTest<NvBlastMessage::Warning, 1> 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<NvBlastAssetDesc> 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<Nv::Blast::Asset&>(*asset); - - NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr); - - std::vector<float> supportChunkHealths(graph.nodeCount); - for (size_t i = 0; i < supportChunkHealths.size(); ++i) - { - supportChunkHealths[i] = 1.0f + (float)i; - } - - std::vector<float> 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<char> scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - Nv::Blast::Actor& actorInt = static_cast<Nv::Blast::Actor&>(*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 <map>
+#include <algorithm>
+
+#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<uint32_t> 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<uint32_t> 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<NvBlastActor*>& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage)
+{
+ std::vector<NvBlastChunkFractureData> chunkEvents; /* num lower-support chunks + bonds */
+ std::vector<NvBlastBondFractureData> bondEvents; /* num lower-support chunks + bonds */
+ chunkEvents.resize(testAsset->solverChunks.size());
+ bondEvents.resize(testAsset->solverBonds.size());
+
+
+ std::vector<char> splitScratch;
+ std::vector<NvBlastActor*> 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<NvBlastActor*>::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();)
+ {
+ NvBlastActor* actor = *k;
+ NvBlastFractureBuffers events = { static_cast<uint32_t>(bondEvents.size()), static_cast<uint32_t>(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<int FailLevel, int Verbosity>
+class ActorTest : public BlastBaseTest<FailLevel, Verbosity>
+{
+public:
+ ActorTest()
+ {
+
+ }
+
+ static void messageLog(int type, const char* msg, const char* file, int line)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line);
+ }
+
+ static void* alloc(size_t size)
+ {
+ return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size);
+ }
+
+ static void free(void* mem)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::alignedFree(mem);
+ }
+
+ NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc)
+ {
+ // fix desc if wrong order or missing coverage first
+ NvBlastAssetDesc fixedDesc = desc;
+ std::vector<NvBlastChunkDesc> chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount);
+ std::vector<NvBlastBondDesc> bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount);
+ std::vector<uint32_t> chunkReorderMap(desc.chunkCount);
+ std::vector<char> 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<char> 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<Nv::Blast::Actor*>&, uint32_t, uint32_t, bool)
+ )
+ {
+ const Nv::Blast::Asset& solverAsset = *static_cast<const Nv::Blast::Asset*>(&asset);
+
+ std::vector<Nv::Blast::Actor*> actors;
+ std::vector<Nv::Blast::Actor*> buffer(NvBlastAssetGetChunkCount(&asset, messageLog));
+
+ // Instance the first actor from the asset
+ actors.push_back(static_cast<Nv::Blast::Actor*>(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<uint32_t>())
+ {
+ 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<Nv::Blast::Actor*>& 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<bool> 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<uint32_t>())
+ {
+ if (isSupport[chunkIndex])
+ {
+ break;
+ }
+ chunkIndex = chunks[chunkIndex].parentChunkIndex;
+ }
+
+ EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex));
+ }
+ else
+ {
+ // Array of visibility flags
+ std::vector<bool> 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<bool> 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<uint32_t>())
+ {
+ // 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<Nv::Blast::Actor*>& 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<Nv::Blast::Actor*>&, 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<const char*>(family1);
+ const char* block2 = reinterpret_cast<const char*>(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<NvBlastActor*>& 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<NvBlastActor*>& actors, NvBlastLog logFn)
+ {
+ if (actors.size())
+ {
+ EXPECT_LT(s_curr, s_storage.size());
+ const NvBlastFamily* family = reinterpret_cast<NvBlastFamily*>(&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<Nv::Blast::Actor*>(actors[0]);
+ const Nv::Blast::Asset* solverAsset = a.getAsset();
+ std::vector<char> storageFamilyCopy((char*)family, (char*)family + size);
+ NvBlastFamily* storageFamily = reinterpret_cast<NvBlastFamily*>(storageFamilyCopy.data());
+ NvBlastFamilySetAsset(storageFamily, solverAsset, logFn);
+ {
+ const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn);
+ std::vector<NvBlastActor*> 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<NvBlastActor*>& actors, NvBlastLog logFn)
+ {
+ if (actors.size() == 0)
+ {
+ return;
+ }
+
+ Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
+ const Nv::Blast::Asset* solverAsset = a.getAsset();
+
+ const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn);
+
+ std::vector< std::vector<char> > 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<char>& 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<size_t> 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<NvBlastActor*>& actors, NvBlastLog logFn)
+ {
+ if (actors.size() <= 1)
+ {
+ return;
+ }
+
+ Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
+ const Nv::Blast::Asset* solverAsset = a.getAsset();
+
+ const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn);
+ const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn);
+ std::vector<char> buffer((char*)oldFamily, (char*)oldFamily + size);
+ NvBlastFamily* familyCopy = reinterpret_cast<NvBlastFamily*>(buffer.data());
+
+ const uint32_t serCount = 1 + (rand() % actors.size() - 1);
+
+ const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn);
+ std::vector<NvBlastActor*> 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<char> > streams(serCount);
+ for (uint32_t i = 0; i < serCount; ++i)
+ {
+ std::vector<char>& 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<NvBlastActor*>&, 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<NvBlastActor*> 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<NvBlastActor*> buffer1(actorCount);
+ const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog);
+ EXPECT_EQ(actorsWritten, actorCount);
+ std::vector<NvBlastActor*> 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<NvBlastActor*>::iterator k = actors.begin(); k != actors.end(); ++k)
+ {
+ actorTest(*static_cast<Nv::Blast::Actor*>(*k), messageLog);
+ }
+ }
+ }
+ if (printActorCount) std::cout << "\n";
+
+ // Test fractured actor set
+ if (postDamageTest)
+ {
+ std::vector<NvBlastActor*> actorArray(actors.begin(), actors.end());
+ postDamageTest(actorArray, messageLog);
+ }
+
+ // Release remaining actors
+ for (std::set<NvBlastActor*>::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<NvBlastAsset*> m_assets;
+ std::vector<NvBlastActor*> m_actors;
+ std::vector<char> m_scratch;
+ static std::vector<char> s_storage;
+
+ static size_t s_curr;
+};
+
+// Static values
+template<int FailLevel, int Verbosity>
+std::vector<char> ActorTest<FailLevel, Verbosity>::s_storage;
+
+template<int FailLevel, int Verbosity>
+size_t ActorTest<FailLevel, Verbosity>::s_curr;
+
+// Specializations
+typedef ActorTest<NvBlastMessage::Error, 1> ActorTestAllowWarnings;
+typedef ActorTest<NvBlastMessage::Warning, 1> 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<NvBlastAssetDesc> 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<Nv::Blast::Asset&>(*asset);
+
+ NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr);
+
+ std::vector<float> supportChunkHealths(graph.nodeCount);
+ for (size_t i = 0; i < supportChunkHealths.size(); ++i)
+ {
+ supportChunkHealths[i] = 1.0f + (float)i;
+ }
+
+ std::vector<float> 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<char> scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog));
+ NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog);
+ EXPECT_TRUE(actor != nullptr);
+
+ Nv::Blast::Actor& actorInt = static_cast<Nv::Blast::Actor&>(*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 index 9bd8aa0..6511f60 100644..100755 --- 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 <algorithm> - - -// 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 <fstream> -#include <iosfwd> - -#ifdef WIN32 -#include <windows.h> -#endif - -template<int FailLevel, int Verbosity> -class AssetTest : public BlastBaseTest<FailLevel, Verbosity> -{ -public: - - AssetTest() - { - NvBlastTkFrameworkCreate(); - } - - ~AssetTest() - { - NvBlastTkFrameworkGet()->release(); - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - BlastBaseTest<FailLevel, Verbosity>::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<const uint32_t*, uint32_t>(a.m_graph.getChunkIndices(), chunkIndexStop, chunkIndex); - EXPECT_EQ(chunkIndexStop, it); - } - } - } - - NvBlastAsset* buildAsset(const ExpectedAssetValues& expected, const NvBlastAssetDesc* desc) - { - std::vector<char> 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<NvBlastChunkDesc> chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount); - shuffledDesc.chunkDescs = chunkDescs.data(); - std::vector<NvBlastBondDesc> bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount); - shuffledDesc.bondDescs = bondDescs.data(); - if (!useTk) - { - std::vector<char> 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<uint32_t> shuffledOrder(chunkDescCount); - for (uint32_t i = 0; i < chunkDescCount; ++i) - { - shuffledOrder[i] = i; - } - - // An array into which to copy the reordered descs - std::vector<NvBlastChunkDesc> shuffledChunkDescs(chunkDescCount); - - std::vector<char> 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<NvBlastBondDesc> 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<uint32_t> chunkReorderMap(chunkDescCount); - std::vector<char> 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<char> 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<uint32_t> chunkIndexOffsets(2); - std::vector<uint32_t> 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<NvBlastBondDesc*>(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs)); - NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(mergedDesc.chunkDescs)); - } - - // Finally free the original asset - NVBLAST_FREE(asset); - } -}; - -typedef AssetTest<-1, 0> AssetTestAllowErrorsSilently; -typedef AssetTest<NvBlastMessage::Error, 0> AssetTestAllowWarningsSilently; -typedef AssetTest<NvBlastMessage::Error, 1> AssetTestAllowWarnings; -typedef AssetTest<NvBlastMessage::Warning, 1> AssetTestStrict; - - -TEST_F(AssetTestStrict, BuildAssets) -{ - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector<NvBlastAsset*> 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<Nv::Blast::Asset*> assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - assets[i] = reinterpret_cast<Nv::Blast::Asset*>(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<Nv::Blast::Asset*> assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - assets[i] = reinterpret_cast<Nv::Blast::Asset*>(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<Nv::Blast::Asset*>(ser->deserializeFromBuffer(buffer, size)); - - //TODO: Compare assets - checkAssetsExpected(*rtAsset, g_assetExpectedValues[i]); - - free(static_cast<void*>(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<char> stream; - - class StreamBufferProvider : public Nv::Blast::ExtSerialization::BufferProvider - { - public: - StreamBufferProvider(std::vector<char>& 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<char>& m_stream; - size_t m_cursor; - } myStreamProvider(stream); - - ser->setBufferProvider(&myStreamProvider); - - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector<Nv::Blast::Asset*> assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - assets[i] = reinterpret_cast<Nv::Blast::Asset*>(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<Nv::Blast::Asset*>(ser->deserializeFromBuffer(buffer, bufferSize)); - EXPECT_TRUE(rtAsset != nullptr); - if (rtAsset == nullptr) - { - break; - } - - //TODO: Compare assets - checkAssetsExpected(*rtAsset, g_assetExpectedValues[assetnum]); - - free(static_cast<void*>(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<NvBlastAsset*> assets(assetDescCount); - - // Build - for (uint32_t i = 0; i < assetDescCount; ++i) - { - const NvBlastAssetDesc* desc = &g_assetDescsMissingCoverage[i]; - NvBlastAssetDesc fixedDesc = *desc; - std::vector<NvBlastChunkDesc> chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount); - std::vector<NvBlastBondDesc> bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount); - std::vector<uint32_t> chunkReorderMap(desc->chunkCount); - std::vector<char> 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 <algorithm>
+
+
+// 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 <fstream>
+#include <iosfwd>
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+template<int FailLevel, int Verbosity>
+class AssetTest : public BlastBaseTest<FailLevel, Verbosity>
+{
+public:
+
+ AssetTest()
+ {
+ NvBlastTkFrameworkCreate();
+ }
+
+ ~AssetTest()
+ {
+ NvBlastTkFrameworkGet()->release();
+ }
+
+ static void messageLog(int type, const char* msg, const char* file, int line)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line);
+ }
+
+ static void* alloc(size_t size)
+ {
+ return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size);
+ }
+
+ static void free(void* mem)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::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<const uint32_t*, uint32_t>(a.m_graph.getChunkIndices(), chunkIndexStop, chunkIndex);
+ EXPECT_EQ(chunkIndexStop, it);
+ }
+ }
+ }
+
+ NvBlastAsset* buildAsset(const ExpectedAssetValues& expected, const NvBlastAssetDesc* desc)
+ {
+ std::vector<char> 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<NvBlastChunkDesc> chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount);
+ shuffledDesc.chunkDescs = chunkDescs.data();
+ std::vector<NvBlastBondDesc> bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount);
+ shuffledDesc.bondDescs = bondDescs.data();
+ if (!useTk)
+ {
+ std::vector<char> 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<uint32_t> shuffledOrder(chunkDescCount);
+ for (uint32_t i = 0; i < chunkDescCount; ++i)
+ {
+ shuffledOrder[i] = i;
+ }
+
+ // An array into which to copy the reordered descs
+ std::vector<NvBlastChunkDesc> shuffledChunkDescs(chunkDescCount);
+
+ std::vector<char> 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<NvBlastBondDesc> 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<uint32_t> chunkReorderMap(chunkDescCount);
+ std::vector<char> 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<char> 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<uint32_t> chunkIndexOffsets(2);
+ std::vector<uint32_t> 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<NvBlastBondDesc*>(mergedDesc.bondDescs));
+ NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs));
+ NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs));
+ NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs));
+ NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs));
+ NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(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<NvBlastBondDesc*>(mergedDesc.bondDescs));
+ NVBLAST_FREE(const_cast<NvBlastChunkDesc*>(mergedDesc.chunkDescs));
+ }
+
+ // Finally free the original asset
+ NVBLAST_FREE(asset);
+ }
+};
+
+typedef AssetTest<-1, 0> AssetTestAllowErrorsSilently;
+typedef AssetTest<NvBlastMessage::Error, 0> AssetTestAllowWarningsSilently;
+typedef AssetTest<NvBlastMessage::Error, 1> AssetTestAllowWarnings;
+typedef AssetTest<NvBlastMessage::Warning, 1> AssetTestStrict;
+
+
+TEST_F(AssetTestStrict, BuildAssets)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ std::vector<NvBlastAsset*> 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<Nv::Blast::Asset*> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = reinterpret_cast<Nv::Blast::Asset*>(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<Nv::Blast::Asset*> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = reinterpret_cast<Nv::Blast::Asset*>(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<Nv::Blast::Asset*>(ser->deserializeFromBuffer(buffer, size));
+
+ //TODO: Compare assets
+ checkAssetsExpected(*rtAsset, g_assetExpectedValues[i]);
+
+ free(static_cast<void*>(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<char> stream;
+
+ class StreamBufferProvider : public Nv::Blast::ExtSerialization::BufferProvider
+ {
+ public:
+ StreamBufferProvider(std::vector<char>& 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<char>& m_stream;
+ size_t m_cursor;
+ } myStreamProvider(stream);
+
+ ser->setBufferProvider(&myStreamProvider);
+
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ std::vector<Nv::Blast::Asset*> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = reinterpret_cast<Nv::Blast::Asset*>(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<Nv::Blast::Asset*>(ser->deserializeFromBuffer(buffer, bufferSize));
+ EXPECT_TRUE(rtAsset != nullptr);
+ if (rtAsset == nullptr)
+ {
+ break;
+ }
+
+ //TODO: Compare assets
+ checkAssetsExpected(*rtAsset, g_assetExpectedValues[assetnum]);
+
+ free(static_cast<void*>(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<NvBlastAsset*> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ const NvBlastAssetDesc* desc = &g_assetDescsMissingCoverage[i];
+ NvBlastAssetDesc fixedDesc = *desc;
+ std::vector<NvBlastChunkDesc> chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount);
+ std::vector<NvBlastBondDesc> bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount);
+ std::vector<uint32_t> chunkReorderMap(desc->chunkCount);
+ std::vector<char> 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 <vector> -#include <algorithm> - -#include "NvBlastExtAuthoringCutoutImpl.h" - - - - -template<int FailLevel, int Verbosity> -class AuthoringCutoutConvertToIncrementalTest : public BlastBaseTest<FailLevel, Verbosity> -{ -public: - AuthoringCutoutConvertToIncrementalTest() - { - - } - - std::vector<Nv::Blast::POINT2D>* generateRectangleTrace(int32_t x1, int32_t x2, int32_t y1, int32_t y2, int32_t start, bool isCCW) - { - auto Ret = new std::vector<Nv::Blast::POINT2D>(); - 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<Nv::Blast::POINT2D> 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<Nv::Blast::POINT2D>()); - *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<FailLevel, Verbosity>::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - - BlastBaseTest<FailLevel, Verbosity>::alignedFree(mem); - } - - std::vector< std::vector<Nv::Blast::POINT2D>* > traces; - std::vector< std::vector<Nv::Blast::POINT2D>* > expectedTraces; - - static std::vector<char> s_storage; - - static size_t s_curr; -}; - -// Static values -template<int FailLevel, int Verbosity> -std::vector<char> AuthoringCutoutConvertToIncrementalTest<FailLevel, Verbosity>::s_storage; - -template<int FailLevel, int Verbosity> -size_t AuthoringCutoutConvertToIncrementalTest<FailLevel, Verbosity>::s_curr; - -// Specializations -typedef AuthoringCutoutConvertToIncrementalTest<NvBlastMessage::Error, 1> AuthoringCutoutConvertToIncrementalTestAllowWarnings; -typedef AuthoringCutoutConvertToIncrementalTest<NvBlastMessage::Warning, 1> 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<std::vector<Nv::Blast::POINT2D>*> 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<Nv::Blast::POINT2D>()); - *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 index cb0481f..14f96fc 100644..100755 --- 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 <algorithm> -#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<uint32_t>(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<const Nv::Blast::Actor*>(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<const Nv::Blast::Actor*>(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<char> 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<char> 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<char> 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<char> 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 <algorithm>
+#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<uint32_t>(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<const Nv::Blast::Actor*>(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<const Nv::Blast::Actor*>(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<char> 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<char> 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<char> 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<char> 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 index d0a872d..bfbea01 100644..100755 --- 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 <stdlib.h> -#include <ostream> -#include <stdint.h> -#include <map> -#include <algorithm> - - -// ==================================================================================================================== -// HELPERS -// ==================================================================================================================== - -::testing::AssertionResult VectorMatch(const std::vector<uint32_t>& 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<int FailLevel, int Verbosity> -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<SupportGraph*>(m_graphMemory.data()); - - m_graph->m_nodeCount = chunkCount; - m_graph->m_chunkIndicesOffset = static_cast<uint32_t>(chunkIndicesOffset); - m_graph->m_adjacencyPartitionOffset = static_cast<uint32_t>(adjacencyPartitionOffset); - m_graph->m_adjacentNodeIndicesOffset = static_cast<uint32_t>(adjacentNodeIndicesOffset); - m_graph->m_adjacentBondIndicesOffset = static_cast<uint32_t>(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<NodeIndex> 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<IslandInfo>& info) - { - IslandId* islandIds = graph.getIslandIds(); - - std::map<IslandId, IslandInfo> islandMap; - - for (NodeIndex n = 0; n < m_graph->m_nodeCount; n++) - { - EXPECT_TRUE(islandIds[n] != invalidIndex<uint32_t>()); - 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<char> m_graphMemory; - std::vector<char> m_memoryBlock; -}; - -typedef FamilyGraphTest<NvBlastMessage::Error, 1> FamilyGraphTestAllowWarnings; -typedef FamilyGraphTest<NvBlastMessage::Warning, 1> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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 <stdlib.h>
+#include <ostream>
+#include <stdint.h>
+#include <map>
+#include <algorithm>
+
+
+// ====================================================================================================================
+// HELPERS
+// ====================================================================================================================
+
+::testing::AssertionResult VectorMatch(const std::vector<uint32_t>& 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<int FailLevel, int Verbosity>
+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<SupportGraph*>(m_graphMemory.data());
+
+ m_graph->m_nodeCount = chunkCount;
+ m_graph->m_chunkIndicesOffset = static_cast<uint32_t>(chunkIndicesOffset);
+ m_graph->m_adjacencyPartitionOffset = static_cast<uint32_t>(adjacencyPartitionOffset);
+ m_graph->m_adjacentNodeIndicesOffset = static_cast<uint32_t>(adjacentNodeIndicesOffset);
+ m_graph->m_adjacentBondIndicesOffset = static_cast<uint32_t>(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<NodeIndex> 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<IslandInfo>& info)
+ {
+ IslandId* islandIds = graph.getIslandIds();
+
+ std::map<IslandId, IslandInfo> islandMap;
+
+ for (NodeIndex n = 0; n < m_graph->m_nodeCount; n++)
+ {
+ EXPECT_TRUE(islandIds[n] != invalidIndex<uint32_t>());
+ 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<char> m_graphMemory;
+ std::vector<char> m_memoryBlock;
+};
+
+typedef FamilyGraphTest<NvBlastMessage::Error, 1> FamilyGraphTestAllowWarnings;
+typedef FamilyGraphTest<NvBlastMessage::Warning, 1> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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<IslandInfo> 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<char> 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 index cd5f936..f3fc8fd 100644..100755 --- 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 <iostream> -#include <memory> -#include "TaskDispatcher.h" - -#include "NvBlastActor.h" -#include "NvBlastExtDamageShaders.h" - -#if NV_XBOXONE -#undef min -#undef max -#endif - -typedef std::function<void(const Nv::Blast::Actor&, NvBlastLog)> ActorTestFunction; -typedef std::function<void(std::vector<NvBlastActor*>&, NvBlastLog)> PostDamageTestFunction; - - -static void blast(std::set<NvBlastActor*>& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) -{ - std::vector<NvBlastChunkFractureData> chunkEvents; /* num lower-support chunks + bonds */ - std::vector<NvBlastBondFractureData> bondEvents; /* num lower-support chunks + bonds */ - chunkEvents.resize(testAsset->solverChunks.size()); - bondEvents.resize(testAsset->solverBonds.size()); - - NvBlastFractureBuffers events = { static_cast<uint32_t>(bondEvents.size()), static_cast<uint32_t>(chunkEvents.size()), bondEvents.data(), chunkEvents.data() }; - - std::vector<float> scratch(chunkEvents.size() + bondEvents.size(), 0.0f); - - std::vector<char> splitScratch; - std::vector<NvBlastActor*> 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<NvBlastActor*>::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<int FailLevel, int Verbosity> -class MultithreadingTest : public BlastBaseTest<FailLevel, Verbosity> -{ -public: - MultithreadingTest() - { - - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - BlastBaseTest<FailLevel, Verbosity>::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<bool> 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<uint32_t>()) - { - if (isSupport[chunkIndex]) - { - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - - EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); - } - else - { - // Array of visibility flags - std::vector<bool> 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<bool> 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<uint32_t>()) - { - // 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<NvBlastActor*>::iterator k = m_actors.begin(); k != m_actors.end(); ++k) - { - m_testFunction(*static_cast<Nv::Blast::Actor*>(*k), messageLog); - } - } - } - - const std::set<NvBlastActor*>& getResult() const { return m_actors; } - - private: - std::set<NvBlastActor*> m_actors; - GeneratorAsset* m_asset; - GeneratorAsset::Vec3 m_localPos; - float m_minRadius; - float m_maxRadius; - float m_compressiveDamage; - ActorTestFunction m_testFunction; - - std::vector<NvBlastActor*> 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<char> 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<NvBlastActor*> 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<DamageActorTask>(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<TaskDispatcher::Task> task) { - const DamageActorTask* damageTask = static_cast<const DamageActorTask*>(task.get()); - const std::set<NvBlastActor*>& 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<NvBlastActor*> actorArray(resultActors.begin(), resultActors.end()); - postDamageTestFunction(actorArray, messageLog); - } - - // Release remaining actors - for (std::set<NvBlastActor*>::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<NvBlastMessage::Error, 1> MultithreadingTestAllowWarnings; -typedef MultithreadingTest<NvBlastMessage::Error, 1> 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 <iostream>
+#include <memory>
+#include "TaskDispatcher.h"
+
+#include "NvBlastActor.h"
+#include "NvBlastExtDamageShaders.h"
+
+#if NV_XBOXONE
+#undef min
+#undef max
+#endif
+
+typedef std::function<void(const Nv::Blast::Actor&, NvBlastLog)> ActorTestFunction;
+typedef std::function<void(std::vector<NvBlastActor*>&, NvBlastLog)> PostDamageTestFunction;
+
+
+static void blast(std::set<NvBlastActor*>& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage)
+{
+ std::vector<NvBlastChunkFractureData> chunkEvents; /* num lower-support chunks + bonds */
+ std::vector<NvBlastBondFractureData> bondEvents; /* num lower-support chunks + bonds */
+ chunkEvents.resize(testAsset->solverChunks.size());
+ bondEvents.resize(testAsset->solverBonds.size());
+
+ NvBlastFractureBuffers events = { static_cast<uint32_t>(bondEvents.size()), static_cast<uint32_t>(chunkEvents.size()), bondEvents.data(), chunkEvents.data() };
+
+ std::vector<float> scratch(chunkEvents.size() + bondEvents.size(), 0.0f);
+
+ std::vector<char> splitScratch;
+ std::vector<NvBlastActor*> 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<NvBlastActor*>::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<int FailLevel, int Verbosity>
+class MultithreadingTest : public BlastBaseTest<FailLevel, Verbosity>
+{
+public:
+ MultithreadingTest()
+ {
+
+ }
+
+ static void messageLog(int type, const char* msg, const char* file, int line)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line);
+ }
+
+ static void* alloc(size_t size)
+ {
+ return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size);
+ }
+
+ static void free(void* mem)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::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<bool> 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<uint32_t>())
+ {
+ if (isSupport[chunkIndex])
+ {
+ break;
+ }
+ chunkIndex = chunks[chunkIndex].parentChunkIndex;
+ }
+
+ EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex));
+ }
+ else
+ {
+ // Array of visibility flags
+ std::vector<bool> 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<bool> 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<uint32_t>())
+ {
+ // 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<NvBlastActor*>::iterator k = m_actors.begin(); k != m_actors.end(); ++k)
+ {
+ m_testFunction(*static_cast<Nv::Blast::Actor*>(*k), messageLog);
+ }
+ }
+ }
+
+ const std::set<NvBlastActor*>& getResult() const { return m_actors; }
+
+ private:
+ std::set<NvBlastActor*> m_actors;
+ GeneratorAsset* m_asset;
+ GeneratorAsset::Vec3 m_localPos;
+ float m_minRadius;
+ float m_maxRadius;
+ float m_compressiveDamage;
+ ActorTestFunction m_testFunction;
+
+ std::vector<NvBlastActor*> 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<char> 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<NvBlastActor*> 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<DamageActorTask>(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<TaskDispatcher::Task> task) {
+ const DamageActorTask* damageTask = static_cast<const DamageActorTask*>(task.get());
+ const std::set<NvBlastActor*>& 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<NvBlastActor*> actorArray(resultActors.begin(), resultActors.end());
+ postDamageTestFunction(actorArray, messageLog);
+ }
+
+ // Release remaining actors
+ for (std::set<NvBlastActor*>::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<NvBlastMessage::Error, 1> MultithreadingTestAllowWarnings;
+typedef MultithreadingTest<NvBlastMessage::Error, 1> 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 index e322202..30c2bf4 100644..100755 --- 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 <map> - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// 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<TkActor*> actors(family->getActorCount()); - family->getActors(actors.data(), static_cast<uint32_t>(actors.size())); - for (auto actor : actors) - { - finalState << actor->getVisibleChunkCount(); - finalState << actor->getGraphNodeCount(); - std::vector<uint32_t> 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<ExtSyncEvent*>& 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<ExtSyncEvent*>& m_syncBuffer; -}; - - -class Client : public Base, public TkEventListener -{ -public: - Client(TkTestStrict* test, std::vector<ExtSyncEvent*>& syncBuffer) : Base(test), m_syncBuffer(syncBuffer) {} - -protected: - - virtual void impl() override - { - ExtSync* sync = ExtSync::create(); - - // fill map - for (auto& family : families) - { - std::vector<TkActor*> actors(family->getActorCount()); - family->getActors(actors.data(), static_cast<uint32_t>(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<uint32_t>(m_syncBuffer.size()), m_group); - - // check map - for (auto& family : families) - { - std::vector<TkActor*> actors(family->getActorCount()); - family->getActors(actors.data(), static_cast<uint32_t>(actors.size())); - std::set<uint32_t> 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<TkSplitEvent>(); - 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<TkFamily*, std::set<uint32_t>> m_actorsPerFamily; - std::vector<ExtSyncEvent*>& m_syncBuffer; -}; - -TEST_F(TkTestStrict, SyncTest1) -{ - this->createFramework(); - - std::vector<ExtSyncEvent*> 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 <map>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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<TkActor*> actors(family->getActorCount());
+ family->getActors(actors.data(), static_cast<uint32_t>(actors.size()));
+ for (auto actor : actors)
+ {
+ finalState << actor->getVisibleChunkCount();
+ finalState << actor->getGraphNodeCount();
+ std::vector<uint32_t> 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<ExtSyncEvent*>& 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<ExtSyncEvent*>& m_syncBuffer;
+};
+
+
+class Client : public Base, public TkEventListener
+{
+public:
+ Client(TkTestStrict* test, std::vector<ExtSyncEvent*>& syncBuffer) : Base(test), m_syncBuffer(syncBuffer) {}
+
+protected:
+
+ virtual void impl() override
+ {
+ ExtSync* sync = ExtSync::create();
+
+ // fill map
+ for (auto& family : families)
+ {
+ std::vector<TkActor*> actors(family->getActorCount());
+ family->getActors(actors.data(), static_cast<uint32_t>(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<uint32_t>(m_syncBuffer.size()), m_group);
+
+ // check map
+ for (auto& family : families)
+ {
+ std::vector<TkActor*> actors(family->getActorCount());
+ family->getActors(actors.data(), static_cast<uint32_t>(actors.size()));
+ std::set<uint32_t> 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<TkSplitEvent>();
+ 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<TkFamily*, std::set<uint32_t>> m_actorsPerFamily;
+ std::vector<ExtSyncEvent*>& m_syncBuffer;
+};
+
+TEST_F(TkTestStrict, SyncTest1)
+{
+ this->createFramework();
+
+ std::vector<ExtSyncEvent*> 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 index 8f55612..96fcc75 100644..100755 --- 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 <map> -#include <random> -#include <algorithm> - -#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<TkActorDesc> m_actorDescs; - std::vector<physx::PxTransform> m_relTMs; - std::vector<TkJointDesc> m_jointDescs; -}; - - -template<int FailLevel, int Verbosity> -class TkCompositeTest : public TkBaseTest<FailLevel, Verbosity> -{ -public: - - // Composite/joint tests - void createAssembly(std::vector<TkActor*>& actors, std::vector<TkJoint*>& 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<TkFamily*> 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<TkFamily*>& 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<TkFamily*> 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<TkActor*> actors(f->getActorCount()); - f->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*>(fw->deserialize(*membuf)); -// f->addListener(tracker); -// families[familyNum] = f; - } - - for (size_t familyNum = 0; familyNum < families.size(); ++familyNum) - { - TkFamily* f = families[familyNum]; - - std::vector<TkActor*> actors(f->getActorCount()); - f->getActors(actors.data(), static_cast<uint32_t>(actors.size())); - for (auto a : actors) - { - tracker.insertActor(a); - - std::vector<TkJoint*> 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<TkFamily*>& families, std::vector<TkActor*>& actors) - { - uint32_t totalActorCount = 0; - for (auto family : families) - { - EXPECT_LE(family->getActorCount() + totalActorCount, actors.size()); - totalActorCount += family->getActors(actors.data() + totalActorCount, static_cast<uint32_t>(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<TkFamily*> families1; - std::vector<TkFamily*> families2; - - // Create one assembly - std::vector<TkActor*> actors1; - std::vector<TkJoint*> joints1; - createAssembly(actors1, joints1, createNRFJoints); - tracker.joints.insert(joints1.begin(), joints1.end()); - - // Create another assembly - std::vector<TkActor*> actors2; - std::vector<TkJoint*> 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<TkFamily*> families; - families.push_back(family); - familySerialization(families, tracker); - family = families[0]; - std::vector<TkActor*> actors(family->getActorCount()); - family->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(family->getActorCount()); - family->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*> 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<uint32_t>(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<TkFamily*> families; - - // Create assembly - std::vector<TkActor*> actors; - std::vector<TkJoint*> 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<TkActor*> a(family->getActorCount()); - family->getActors(a.data(), static_cast<uint32_t>(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<FailLevel, Verbosity>::testAssets; - using TkBaseTest<FailLevel, Verbosity>::m_taskman; - using TkBaseTest<FailLevel, Verbosity>::m_groupTM; - using TkBaseTest<FailLevel, Verbosity>::createFramework; - using TkBaseTest<FailLevel, Verbosity>::releaseFramework; - using TkBaseTest<FailLevel, Verbosity>::createTestAssets; - using TkBaseTest<FailLevel, Verbosity>::releaseTestAssets; - using TkBaseTest<FailLevel, Verbosity>::getCubeSlicerProgram; - using TkBaseTest<FailLevel, Verbosity>::getDefaultMaterial; - using TkBaseTest<FailLevel, Verbosity>::getRadialDamageDesc; - using TkBaseTest<FailLevel, Verbosity>::getFalloffProgram; -}; - - -typedef TkCompositeTest<NvBlastMessage::Error, 1> TkCompositeTestAllowWarnings; -typedef TkCompositeTest<NvBlastMessage::Error, 1> 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 <map>
+#include <random>
+#include <algorithm>
+
+#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<TkActorDesc> m_actorDescs;
+ std::vector<physx::PxTransform> m_relTMs;
+ std::vector<TkJointDesc> m_jointDescs;
+};
+
+
+template<int FailLevel, int Verbosity>
+class TkCompositeTest : public TkBaseTest<FailLevel, Verbosity>
+{
+public:
+
+ // Composite/joint tests
+ void createAssembly(std::vector<TkActor*>& actors, std::vector<TkJoint*>& 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<TkFamily*> 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<TkFamily*>& 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<TkFamily*> 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<TkActor*> actors(f->getActorCount());
+ f->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*>(fw->deserialize(*membuf));
+// f->addListener(tracker);
+// families[familyNum] = f;
+ }
+
+ for (size_t familyNum = 0; familyNum < families.size(); ++familyNum)
+ {
+ TkFamily* f = families[familyNum];
+
+ std::vector<TkActor*> actors(f->getActorCount());
+ f->getActors(actors.data(), static_cast<uint32_t>(actors.size()));
+ for (auto a : actors)
+ {
+ tracker.insertActor(a);
+
+ std::vector<TkJoint*> 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<TkFamily*>& families, std::vector<TkActor*>& actors)
+ {
+ uint32_t totalActorCount = 0;
+ for (auto family : families)
+ {
+ EXPECT_LE(family->getActorCount() + totalActorCount, actors.size());
+ totalActorCount += family->getActors(actors.data() + totalActorCount, static_cast<uint32_t>(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<TkFamily*> families1;
+ std::vector<TkFamily*> families2;
+
+ // Create one assembly
+ std::vector<TkActor*> actors1;
+ std::vector<TkJoint*> joints1;
+ createAssembly(actors1, joints1, createNRFJoints);
+ tracker.joints.insert(joints1.begin(), joints1.end());
+
+ // Create another assembly
+ std::vector<TkActor*> actors2;
+ std::vector<TkJoint*> 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<TkFamily*> families;
+ families.push_back(family);
+ familySerialization(families, tracker);
+ family = families[0];
+ std::vector<TkActor*> actors(family->getActorCount());
+ family->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(family->getActorCount());
+ family->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*> 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<uint32_t>(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<TkFamily*> families;
+
+ // Create assembly
+ std::vector<TkActor*> actors;
+ std::vector<TkJoint*> 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<TkActor*> a(family->getActorCount());
+ family->getActors(a.data(), static_cast<uint32_t>(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<FailLevel, Verbosity>::testAssets;
+ using TkBaseTest<FailLevel, Verbosity>::m_taskman;
+ using TkBaseTest<FailLevel, Verbosity>::m_groupTM;
+ using TkBaseTest<FailLevel, Verbosity>::createFramework;
+ using TkBaseTest<FailLevel, Verbosity>::releaseFramework;
+ using TkBaseTest<FailLevel, Verbosity>::createTestAssets;
+ using TkBaseTest<FailLevel, Verbosity>::releaseTestAssets;
+ using TkBaseTest<FailLevel, Verbosity>::getCubeSlicerProgram;
+ using TkBaseTest<FailLevel, Verbosity>::getDefaultMaterial;
+ using TkBaseTest<FailLevel, Verbosity>::getRadialDamageDesc;
+ using TkBaseTest<FailLevel, Verbosity>::getFalloffProgram;
+};
+
+
+typedef TkCompositeTest<NvBlastMessage::Error, 1> TkCompositeTestAllowWarnings;
+typedef TkCompositeTest<NvBlastMessage::Error, 1> 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 index e07d4a0..9687c68 100644..100755 --- 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 <map> -#include <random> -#include <algorithm> - -#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<TkFamily*>& families, std::map<TkFamily*, ExpectedVisibleChunks>& expectedVisibleChunks) -{ - size_t numActors = 0; - for (TkFamily* fam : families) - { - auto ex = expectedVisibleChunks[fam]; - EXPECT_EQ(ex.numActors, fam->getActorCount()); - numActors += ex.numActors; - std::vector<TkActor*> actors(fam->getActorCount()); - fam->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*> families; - TkFamily* trackedFamily; - std::map<TkFamily*, ExpectedVisibleChunks> 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<TkActor*> actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*> families(2); - std::map<TkFamily*, ExpectedVisibleChunks> 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<TkGroup*, TkFamily*> pair; - std::set<pair> 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<TkActor*> 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<std::function<void(void)>> 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<TkActor*> actors(family->getActorCount()); - family->getActors(actors.data(), static_cast<uint32_t>(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<TkAsset*> assets(assetDescCount); - - // assets - for (uint32_t i = 0; i < assetDescCount; ++i) - { - TkAssetDesc desc; - reinterpret_cast<NvBlastAssetDesc&>(desc) = g_assetDescs[i]; - desc.bondFlags = nullptr; - assets[i] = framework->createAsset(desc); - EXPECT_TRUE(assets[i] != nullptr); - } - - // actors - std::vector<TkActor*> actors;; - std::vector<TkFamily*> 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<TkObject*> objects; - - // assets - { - const TkType* assetType = framework->getType(TkTypeIndex::Asset); - objects.resize(framework->getObjectCount(*assetType)); - EXPECT_TRUE(framework->getObjects(reinterpret_cast<TkIdentifiable**>(objects.data()), static_cast<uint32_t>(objects.size()), *assetType) == static_cast<uint32_t>(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<TkIdentifiable**>(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<TkIdentifiable**>(objects.data()), static_cast<uint32_t>(objects.size()), *familyType) == static_cast<uint32_t>(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<int FailMask, int Verbosity> -TkFamily* TkBaseTest<FailMask, Verbosity>::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<TkFamily*>(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<std::function<void(TkActor* a)>> 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<TkActor*> actors(64); - - for (uint32_t i = 0; i < damageCount; i++) - { - actors.resize(family->getActorCount()); - family->getActors(actors.data(), static_cast<uint32_t>(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<TkFractureCommands>()->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<TkFractureEvents>()->tkActorData; - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - } - break; - - case TkSplitEvent::EVENT_TYPE: - { - const TkSplitEvent* split = event.getPayload<TkSplitEvent>(); - - 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<TkFractureCommands>()->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<TkFractureEvents>()->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<TkSplitEvent>(); - 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 <map>
+#include <random>
+#include <algorithm>
+
+#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<TkFamily*>& families, std::map<TkFamily*, ExpectedVisibleChunks>& expectedVisibleChunks)
+{
+ size_t numActors = 0;
+ for (TkFamily* fam : families)
+ {
+ auto ex = expectedVisibleChunks[fam];
+ EXPECT_EQ(ex.numActors, fam->getActorCount());
+ numActors += ex.numActors;
+ std::vector<TkActor*> actors(fam->getActorCount());
+ fam->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*> families;
+ TkFamily* trackedFamily;
+ std::map<TkFamily*, ExpectedVisibleChunks> 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<TkActor*> actors(trackedFamily->getActorCount());
+ trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount());
+ trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount());
+ trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount());
+ trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkActor*> actors(trackedFamily->getActorCount());
+ trackedFamily->getActors(actors.data(), static_cast<uint32_t>(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<TkFamily*> families(2);
+ std::map<TkFamily*, ExpectedVisibleChunks> 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<TkGroup*, TkFamily*> pair;
+ std::set<pair> 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<TkActor*> 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<std::function<void(void)>> 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<TkActor*> actors(family->getActorCount());
+ family->getActors(actors.data(), static_cast<uint32_t>(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<TkAsset*> assets(assetDescCount);
+
+ // assets
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ TkAssetDesc desc;
+ reinterpret_cast<NvBlastAssetDesc&>(desc) = g_assetDescs[i];
+ desc.bondFlags = nullptr;
+ assets[i] = framework->createAsset(desc);
+ EXPECT_TRUE(assets[i] != nullptr);
+ }
+
+ // actors
+ std::vector<TkActor*> actors;;
+ std::vector<TkFamily*> 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<TkObject*> objects;
+
+ // assets
+ {
+ const TkType* assetType = framework->getType(TkTypeIndex::Asset);
+ objects.resize(framework->getObjectCount(*assetType));
+ EXPECT_TRUE(framework->getObjects(reinterpret_cast<TkIdentifiable**>(objects.data()), static_cast<uint32_t>(objects.size()), *assetType) == static_cast<uint32_t>(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<TkIdentifiable**>(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<TkIdentifiable**>(objects.data()), static_cast<uint32_t>(objects.size()), *familyType) == static_cast<uint32_t>(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<int FailMask, int Verbosity>
+TkFamily* TkBaseTest<FailMask, Verbosity>::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<TkFamily*>(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<std::function<void(TkActor* a)>> 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<TkActor*> actors(64);
+
+ for (uint32_t i = 0; i < damageCount; i++)
+ {
+ actors.resize(family->getActorCount());
+ family->getActors(actors.data(), static_cast<uint32_t>(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<TkFractureCommands>()->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<TkFractureEvents>()->tkActorData;
+ EXPECT_EQ((void*)'root', actor.userData);
+ EXPECT_EQ(0, actor.index);
+ }
+ break;
+
+ case TkSplitEvent::EVENT_TYPE:
+ {
+ const TkSplitEvent* split = event.getPayload<TkSplitEvent>();
+
+ 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<TkFractureCommands>()->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<TkFractureEvents>()->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<TkSplitEvent>();
+ 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();
+}
+
|