aboutsummaryrefslogtreecommitdiff
path: root/test/src/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/src/unit')
-rwxr-xr-x[-rw-r--r--]test/src/unit/APITests.cpp3446
-rwxr-xr-x[-rw-r--r--]test/src/unit/ActorTests.cpp2236
-rwxr-xr-x[-rw-r--r--]test/src/unit/AssetTests.cpp1716
-rw-r--r--test/src/unit/AuthoringCutout.cpp410
-rwxr-xr-x[-rw-r--r--]test/src/unit/CoreTests.cpp866
-rwxr-xr-x[-rw-r--r--]test/src/unit/FamilyGraphTests.cpp810
-rwxr-xr-x[-rw-r--r--]test/src/unit/MultithreadingTests.cpp840
-rwxr-xr-x[-rw-r--r--]test/src/unit/SyncTests.cpp688
-rwxr-xr-x[-rw-r--r--]test/src/unit/TkCompositeTests.cpp1550
-rwxr-xr-x[-rw-r--r--]test/src/unit/TkTests.cpp3214
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();
+}
+