From 7115f60b91b5717d90f643fd692010905c7004db Mon Sep 17 00:00:00 2001 From: Bryan Galdrikian Date: Thu, 31 May 2018 11:36:08 -0700 Subject: Blast 1.1.3. See docs/release_notes.txt. --- test/src/unit/ActorTests.cpp | 2236 +++++++++++++++++++++--------------------- 1 file changed, 1118 insertions(+), 1118 deletions(-) mode change 100644 => 100755 test/src/unit/ActorTests.cpp (limited to 'test/src/unit/ActorTests.cpp') diff --git a/test/src/unit/ActorTests.cpp b/test/src/unit/ActorTests.cpp old mode 100644 new mode 100755 index d4de648..79bb1fc --- a/test/src/unit/ActorTests.cpp +++ b/test/src/unit/ActorTests.cpp @@ -1,1118 +1,1118 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and related documentation and -// any modifications thereto. Any use, reproduction, disclosure, or -// distribution of this software and related documentation without an express -// license agreement from NVIDIA Corporation is strictly prohibited. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "BlastBaseTest.h" -#include "AssetGenerator.h" - -#include -#include - -#include "NvBlastActor.h" -#include "NvBlastExtDamageShaders.h" - - -static bool chooseRandomGraphNodes(uint32_t* g, uint32_t count, const Nv::Blast::Actor& actor) -{ - const uint32_t graphNodeCount = actor.getGraphNodeCount(); - - if (graphNodeCount < count) - { - return false; - } - - std::vector graphNodeIndices(graphNodeCount); - uint32_t* index = graphNodeIndices.data(); - for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i ; ++i) - { - *index++ = (uint32_t)i; - } - struct UserDataSorter - { - UserDataSorter(const Nv::Blast::Actor& actor) : m_asset(*actor.getAsset()) {} - - bool operator () (uint32_t i0, uint32_t i1) const - { - const uint32_t c0 = m_asset.m_graph.getChunkIndices()[i0]; - const uint32_t c1 = m_asset.m_graph.getChunkIndices()[i1]; - if (Nv::Blast::isInvalidIndex(c0) || Nv::Blast::isInvalidIndex(c1)) - { - return c0 < c1; - } - return m_asset.getChunks()[c0].userData < m_asset.getChunks()[c1].userData; - } - - const Nv::Blast::Asset& m_asset; - } userDataSorter(actor); - std::sort(graphNodeIndices.data(), graphNodeIndices.data() + graphNodeCount, userDataSorter); - -#if 0 - std::vector descUserData(graphNodeCount); - for (uint32_t i = 0; i < graphNodeCount; ++i) - { - descUserData[i] = actor.getAsset()->m_chunks[actor.getAsset()->m_graph.m_chunkIndices[graphNodeIndices[i]]].userData; - } -#endif - - uint32_t t = 0; - uint32_t m = 0; - for (uint32_t i = 0; i < graphNodeCount && m < count; ++i, ++t) - { - NVBLAST_ASSERT(t < graphNodeCount); - if (t >= graphNodeCount) - { - break; - } - const float U = (float)rand()/RAND_MAX; // U is uniform random number in [0,1) - if ((graphNodeCount - t)*U < count - m) - { - g[m++] = graphNodeIndices[i]; - } - } - - return m == count; -} - - -static void blast(std::set& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) -{ - std::vector chunkEvents; /* num lower-support chunks + bonds */ - std::vector bondEvents; /* num lower-support chunks + bonds */ - chunkEvents.resize(testAsset->solverChunks.size()); - bondEvents.resize(testAsset->solverBonds.size()); - - - std::vector splitScratch; - std::vector newActorsBuffer(testAsset->solverChunks.size()); - - NvBlastExtRadialDamageDesc damage = { - compressiveDamage, - { localPos.x, localPos.y, localPos.z }, - minRadius, - maxRadius - }; - - NvBlastExtProgramParams programParams = - { - &damage, - nullptr - }; - - NvBlastDamageProgram program = { - NvBlastExtFalloffGraphShader, - nullptr - }; - - size_t totalNewActorsCount = 0; - for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) - { - NvBlastActor* actor = *k; - NvBlastFractureBuffers events = { static_cast(bondEvents.size()), static_cast(chunkEvents.size()), bondEvents.data(), chunkEvents.data() }; - - NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr); - NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr); - const bool isDamaged = NvBlastActorIsSplitRequired(actor, nullptr); - bool removeActor = false; - - if (events.bondFractureCount + events.chunkFractureCount > 0) - { - NvBlastActorSplitEvent splitEvent; - splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount]; - uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount); - - splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); - const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr); - EXPECT_TRUE(isDamaged || newActorsCount == 0); - totalNewActorsCount += newActorsCount; - removeActor = splitEvent.deletedActor != NULL; - } - else - { - EXPECT_FALSE(isDamaged); - } - - if (removeActor) - { - k = actorsToDamage.erase(k); - } - else - { - ++k; - } - } - - for (size_t i = 0; i < totalNewActorsCount; ++i) - { - actorsToDamage.insert(newActorsBuffer[i]); - } -} - - -template -class ActorTest : public BlastBaseTest -{ -public: - ActorTest() - { - - } - - static void messageLog(int type, const char* msg, const char* file, int line) - { - BlastBaseTest::messageLog(type, msg, file, line); - } - - static void* alloc(size_t size) - { - return BlastBaseTest::alignedZeroedAlloc(size); - } - - static void free(void* mem) - { - BlastBaseTest::alignedFree(mem); - } - - NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc) - { - // fix desc if wrong order or missing coverage first - NvBlastAssetDesc fixedDesc = desc; - std::vector chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount); - std::vector bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount); - std::vector chunkReorderMap(desc.chunkCount); - std::vector scratch(desc.chunkCount * sizeof(NvBlastChunkDesc)); - NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog); - NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog); - fixedDesc.chunkDescs = chunkDescs.data(); - fixedDesc.bondDescs = bondDescs.empty() ? nullptr : bondDescs.data(); - - // create asset - m_scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&fixedDesc, messageLog)); - void* mem = alloc(NvBlastGetAssetMemorySize(&fixedDesc, messageLog)); - NvBlastAsset* asset = NvBlastCreateAsset(mem, &fixedDesc, m_scratch.data(), messageLog); - EXPECT_TRUE(asset != nullptr); - return asset; - } - - void buildAssets() - { - m_assets.resize(getAssetDescCount()); - for (uint32_t i = 0; i < m_assets.size(); ++i) - { - m_assets[i] = buildAsset(g_assetDescs[i]); - } - } - - NvBlastActor* instanceActor(const NvBlastAsset& asset) - { - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(&asset, nullptr)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, &asset, nullptr); - std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - return actor; - } - - void instanceActors() - { - m_actors.resize(m_assets.size()); - for (uint32_t i = 0; i < m_actors.size(); ++i) - { - m_actors[i] = instanceActor(*m_assets[i]); - } - } - - void releaseActors() - { - for (uint32_t i = 0; i < m_actors.size(); ++i) - { - NvBlastFamily* family = NvBlastActorGetFamily(m_actors[i], messageLog); - - const bool actorReleaseResult = NvBlastActorDeactivate(m_actors[i], messageLog); - EXPECT_TRUE(actorReleaseResult); - - free(family); - } - } - - void destroyAssets() - { - for (uint32_t i = 0; i < m_assets.size(); ++i) - { - free(m_assets[i]); - } - } - - void instanceAndPartitionRecursively - ( - const NvBlastAsset& asset, - bool partitionToSubsupport, - void (*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), - void (*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool) - ) - { - const Nv::Blast::Asset& solverAsset = *static_cast(&asset); - - std::vector actors; - std::vector buffer(NvBlastAssetGetChunkCount(&asset, messageLog)); - - // Instance the first actor from the asset - actors.push_back(static_cast(instanceActor(asset))); - - NvBlastFamily* family = NvBlastActorGetFamily(actors[0], messageLog); - - const uint32_t supportChunkCount = NvBlastAssetGetSupportChunkCount(&asset, messageLog); - const uint32_t leafChunkCount = actors[0]->getAsset()->m_leafChunkCount; - - // Now randomly partition the actors in the array, and keep going until we're down to single support chunks - bool canFracture = true; - - while (canFracture) - { - canFracture = false; - - for (uint32_t actorToPartition = 0; actorToPartition < actors.size(); ++actorToPartition) - { - Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[actorToPartition]; - if (a == nullptr) - { - continue; - } - - m_scratch.reserve((size_t)NvBlastActorGetRequiredScratchForSplit(a, messageLog)); - - if (preSplitTest) - { - preSplitTest(*a, nullptr); - } - - const bool singleLowerSupportChunk = a->getGraphNodeCount() <= 1; - uint32_t newActorCount = 0; - - for (int damageNum = 0; newActorCount < 2 && damageNum < 100; ++damageNum) // Avoid infinite loops - { - if (!singleLowerSupportChunk) - { - uint32_t g[2]; - chooseRandomGraphNodes(g, 2, *a); - const uint32_t bondIndex = solverAsset.m_graph.findBond(g[0], g[1]); - if (bondIndex != Nv::Blast::invalidIndex()) - { - a->damageBond(g[0], g[1], bondIndex, 100.0f); - a->findIslands(m_scratch.data()); - } - } - else - if (!partitionToSubsupport) - { - continue; - } - - // Split actor - newActorCount = a->partition((Nv::Blast::Actor**)&buffer[0], (uint32_t)buffer.size(), messageLog); - - if (newActorCount >= 2) - { - actors[actorToPartition] = nullptr; - } - } - - if (newActorCount > 1) - { - canFracture = true; - } - - for (uint32_t i = 0; i < newActorCount; ++i) - { - actors.push_back(buffer[i]); - buffer[i]->updateVisibleChunksFromGraphNodes(); - } - } - } - - if (postSplitTest) - { - postSplitTest(actors, leafChunkCount, supportChunkCount, partitionToSubsupport); - } - - for (auto actor : actors) - { - if (actor) - actor->release(); - } - - free(family); - } - - static void recursivePartitionPostSplitTestCounts(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) - { - // Test to see that all actors are split down to single support chunks - uint32_t remainingActorCount = 0; - for (uint32_t i = 0; i < actors.size(); ++i) - { - Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; - if (a == nullptr) - { - continue; - } - - ++remainingActorCount; - - NVBLAST_ASSERT(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); - EXPECT_TRUE(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); - if (!partitionToSubsupport) - { - EXPECT_EQ(1, a->getGraphNodeCount()); - } - - if (0 == a->getVisibleChunkCount()) - { - EXPECT_TRUE(a->isBoundToWorld()); - EXPECT_EQ(1, a->getGraphNodeCount()); - EXPECT_EQ(a->getFamilyHeader()->m_asset->m_graph.m_nodeCount - 1, a->getFirstGraphNodeIndex()); - --remainingActorCount; // Do not count this as a remaining actor, to be compared with leaf or support chunk counts later - } - - const bool actorReleaseResult = NvBlastActorDeactivate(actors[i], nullptr); - EXPECT_TRUE(actorReleaseResult); - } - - if (partitionToSubsupport) - { - EXPECT_EQ(leafChunkCount, remainingActorCount); - } - else - { - EXPECT_EQ(supportChunkCount, remainingActorCount); - } - } - - static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog) - { - const Nv::Blast::Asset& asset = *actor.getAsset(); - const NvBlastChunk* chunks = asset.getChunks(); - - if (actor.isSubSupportChunk()) - { - EXPECT_EQ(1, actor.getVisibleChunkCount()); - - const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor); - - EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex); - - // Make sure the visible chunk is subsupport - // Array of support flags - std::vector isSupport(asset.m_chunkCount, false); - for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i) - { - const uint32_t chunkIndex = asset.m_graph.getChunkIndices()[i]; - if (!Nv::Blast::isInvalidIndex(chunkIndex)) - { - isSupport[chunkIndex] = true; - } - } - - // Climb hierarchy to find support chunk - uint32_t chunkIndex = firstVisibleChunkIndex; - while (chunkIndex != Nv::Blast::invalidIndex()) - { - if (isSupport[chunkIndex]) - { - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - - EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); - } - else - { - // Array of visibility flags - std::vector isVisible(asset.m_chunkCount, false); - for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i) - { - isVisible[(uint32_t)i] = true; - } - - // Mark visible nodes representing graph chunks - std::vector visibleChunkFound(asset.m_chunkCount, false); - - // Make sure every graph chunk is represented by a visible chunk, or represents the world - for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i) - { - const uint32_t graphNodeIndex = (uint32_t)i; - uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex]; - // Climb hierarchy to find visible chunk - while (chunkIndex != Nv::Blast::invalidIndex()) - { - // Check that chunk owners are accurate - EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]); - if (isVisible[chunkIndex]) - { - visibleChunkFound[chunkIndex] = true; - break; - } - chunkIndex = chunks[chunkIndex].parentChunkIndex; - } - EXPECT_TRUE(!Nv::Blast::isInvalidIndex(chunkIndex) || (graphNodeIndex == asset.m_graph.m_nodeCount-1 && actor.isBoundToWorld())); - } - - // Check that all visible chunks are accounted for - for (uint32_t i = 0; i < asset.m_chunkCount; ++i) - { - EXPECT_EQ(visibleChunkFound[i], isVisible[i]); - } - - // Make sure that, if all siblings are intact, they are invisible - for (uint32_t i = 0; i < asset.m_chunkCount; ++i) - { - bool allIntact = true; - bool noneVisible = true; - if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport - { - for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j) - { - allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex(); - noneVisible = noneVisible && !isVisible[j]; - } - EXPECT_TRUE(!allIntact || noneVisible); - } - } - } - } - - static void recursivePartitionPostSplitTestVisibleChunks(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) - { - for (uint32_t i = 0; i < actors.size(); ++i) - { - Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; - if (a == nullptr) - { - continue; - } - - testActorVisibleChunks(*a, nullptr); - } - } - - void partitionActorsToSupportChunks - ( - uint32_t assetDescCount, - const NvBlastAssetDesc* assetDescs, - void(*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), - void(*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool), - bool partitionToSubsupport - ) - { - srand(0); - - for (uint32_t i = 0; i < assetDescCount; ++i) - { - // Create an asset - NvBlastAsset* asset = buildAsset(assetDescs[i]); - - // Perform repeated partitioning - instanceAndPartitionRecursively(*asset, partitionToSubsupport, preSplitTest, postSplitTest); - - // Free the asset - free(asset); - } - } - - static void compareFamilies(const NvBlastFamily* family1, const NvBlastFamily* family2, size_t size, NvBlastLog logFn) - { - const char* block1 = reinterpret_cast(family1); - const char* block2 = reinterpret_cast(family2); -#if 0 - EXPECT_EQ(0, memcmp(block1, block2, size)); -#else - bool diffFound = false; - size_t startDiff = 0; - for (size_t i = 0; i < size; ++i) - { - if (block1[i] != block2[i]) - { - diffFound = true; - startDiff = i; - break; - } - } - if (!diffFound) - { - return; - } - size_t endDiff = startDiff; - for (size_t i = size; i--;) - { - if (block1[i] != block2[i]) - { - endDiff = i; - break; - } - } - std::ostringstream msg; - msg << "Block deserialization does not match current block in position range [" << startDiff << ", " << endDiff << "]."; - logFn(NvBlastMessage::Error, msg.str().c_str(), __FILE__, __LINE__); -#endif - } - - static void testActorBlockSerialize(std::vector& actors, NvBlastLog logFn) - { - if (actors.size()) - { - const NvBlastFamily* family = NvBlastActorGetFamily(actors[0], logFn); - const uint32_t size = NvBlastFamilyGetSize(family, logFn); - s_storage.insert(s_storage.end(), (char*)family, (char*)family + size); - } - } - - static void testActorBlockDeserialize(std::vector& actors, NvBlastLog logFn) - { - if (actors.size()) - { - EXPECT_LT(s_curr, s_storage.size()); - const NvBlastFamily* family = reinterpret_cast(&s_storage[s_curr]); - const uint32_t size = NvBlastFamilyGetSize(family, logFn); - EXPECT_LE(s_curr + size, s_storage.size()); - s_curr += size; - const NvBlastFamily* actorFamily = NvBlastActorGetFamily(actors[0], logFn); - // Family may contain different assets pointers, copy into new family block and set the same asset before comparing - Nv::Blast::Actor& a = *static_cast(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - std::vector storageFamilyCopy((char*)family, (char*)family + size); - NvBlastFamily* storageFamily = reinterpret_cast(storageFamilyCopy.data()); - NvBlastFamilySetAsset(storageFamily, solverAsset, logFn); - { - const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn); - std::vector blockActors(actorCountExpected); - const uint32_t actorCountReturned = NvBlastFamilyGetActors(blockActors.data(), actorCountExpected, storageFamily, logFn); - EXPECT_EQ(actorCountExpected, actorCountReturned); - } - compareFamilies(storageFamily, actorFamily, size, logFn); - } - } - - // Serialize all actors and then deserialize back into a new family in a random order, and compare with the original family - static void testActorSerializationNewFamily(std::vector& actors, NvBlastLog logFn) - { - if (actors.size() == 0) - { - return; - } - - Nv::Blast::Actor& a = *static_cast(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - - const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); - - std::vector< std::vector > streams(actors.size()); - for (size_t i = 0; i < actors.size(); ++i) - { - const uint32_t serSize = NvBlastActorGetSerializationSize(actors[i], logFn); - EXPECT_GE(serSizeBound, serSize); - std::vector& stream = streams[i]; - stream.resize(serSize); - const uint32_t bytesWritten = NvBlastActorSerialize(stream.data(), serSize, actors[i], logFn); - EXPECT_EQ(serSize, bytesWritten); - } - - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(solverAsset, logFn)); - NvBlastFamily* newFamily = NvBlastAssetCreateFamily(fmem, solverAsset, logFn); - - std::vector order(actors.size()); - for (size_t i = 0; i < order.size(); ++i) - { - order[i] = i; - } - std::random_shuffle(order.begin(), order.end()); - - for (size_t i = 0; i < actors.size(); ++i) - { - NvBlastActor* newActor = NvBlastFamilyDeserializeActor(newFamily, streams[order[i]].data(), logFn); - EXPECT_TRUE(newActor != nullptr); - } - - const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); - compareFamilies(oldFamily, newFamily, NvBlastFamilyGetSize(oldFamily, logFn), logFn); - - free(newFamily); - } - - // Copy the family and then serialize some subset of actors, deleting them afterwards. - // Then, deserialize back into the block and compare the original and new families. - static void testActorSerializationPartialBlock(std::vector& actors, NvBlastLog logFn) - { - if (actors.size() <= 1) - { - return; - } - - Nv::Blast::Actor& a = *static_cast(actors[0]); - const Nv::Blast::Asset* solverAsset = a.getAsset(); - - const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); - const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn); - std::vector buffer((char*)oldFamily, (char*)oldFamily + size); - NvBlastFamily* familyCopy = reinterpret_cast(buffer.data()); - - const uint32_t serCount = 1 + (rand() % actors.size() - 1); - - const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn); - std::vector actorsRemaining(actorCount); - const uint32_t actorsInFamily = NvBlastFamilyGetActors(&actorsRemaining[0], actorCount, familyCopy, logFn); - EXPECT_EQ(actorCount, actorsInFamily); - - const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); - - std::vector< std::vector > streams(serCount); - for (uint32_t i = 0; i < serCount; ++i) - { - std::vector& stream = streams[i]; - const uint32_t indexToStream = rand() % actorsRemaining.size(); - NvBlastActor* actorToStream = actorsRemaining[indexToStream]; - std::swap(actorsRemaining[indexToStream], actorsRemaining[actorsRemaining.size() - 1]); - actorsRemaining.pop_back(); - const uint32_t serSize = NvBlastActorGetSerializationSize(actorToStream, logFn); - EXPECT_GE(serSizeBound, serSize); - stream.resize(serSize); - const uint32_t bytesWritten = NvBlastActorSerialize(&stream[0], serSize, actorToStream, logFn); - EXPECT_EQ(serSize, bytesWritten); - NvBlastActorDeactivate(actorToStream, logFn); - } - - for (uint32_t i = 0; i < serCount; ++i) - { - NvBlastActor* newActor = NvBlastFamilyDeserializeActor(familyCopy, streams[i].data(), logFn); - EXPECT_TRUE(newActor != nullptr); - } - - compareFamilies(oldFamily, familyCopy, size, logFn); - } - - void damageLeafSupportActors - ( - uint32_t assetCount, - uint32_t familyCount, - uint32_t damageCount, - bool simple, - void (*actorTest)(const Nv::Blast::Actor&, NvBlastLog), - void (*postDamageTest)(std::vector&, NvBlastLog), - CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::BondFlags::ALL_INTERNAL_BONDS - ) - { - const float relativeDamageRadius = simple ? 0.75f : 0.2f; - const float compressiveDamage = 1.0f; - const uint32_t minChunkCount = simple ? 9 : 100; - const uint32_t maxChunkCount = simple ? 9 : 10000; - const bool printActorCount = false; - - srand(0); - - std::cout << "Asset # (out of " << assetCount << "): "; - for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) - { - std::cout << assetNum + 1 << ".. "; - CubeAssetGenerator::Settings settings; - settings.extents = GeneratorAsset::Vec3(1, 1, 1); - settings.bondFlags = bondFlags; - CubeAssetGenerator::DepthInfo depthInfo; - depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); - depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; - settings.depths.push_back(depthInfo); - uint32_t chunkCount = 1; - while (chunkCount < minChunkCount) - { - uint32_t chunkMul; - do - { - depthInfo.slicesPerAxis = simple ? GeneratorAsset::Vec3(2, 2, 2) : GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); - chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); - } while (chunkMul == 1); - if (chunkCount*chunkMul > maxChunkCount) - { - break; - } - chunkCount *= chunkMul; - settings.depths.push_back(depthInfo); - settings.extents = settings.extents * depthInfo.slicesPerAxis; - } - settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support - - // Make largest direction unit size - settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); - - // Create asset - GeneratorAsset testAsset; - CubeAssetGenerator::generate(testAsset, settings); - - NvBlastAssetDesc desc; - desc.chunkDescs = testAsset.solverChunks.data(); - desc.chunkCount = (uint32_t)testAsset.solverChunks.size(); - desc.bondDescs = testAsset.solverBonds.data(); - desc.bondCount = (uint32_t)testAsset.solverBonds.size(); - NvBlastAsset* asset = buildAsset(desc); - NvBlastID assetID = NvBlastAssetGetID(asset, messageLog); - - // copy asset (for setAsset testing) - const char* data = (const char*)asset; - const uint32_t dataSize = NvBlastAssetGetSize(asset, messageLog); - char* duplicateData = (char*)alloc(dataSize); - memcpy(duplicateData, data, dataSize); - NvBlastAsset* assetDuplicate = (NvBlastAsset*)duplicateData; - - // Generate families - for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) - { - // family - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); // Using zeroingAlloc in case actorTest compares memory blocks - NvBlastID id = NvBlastFamilyGetAssetID(family, messageLog); - EXPECT_TRUE(!memcmp(&assetID, &id, sizeof(NvBlastID))); - if (rand() % 2 == 0) - { - // replace asset with duplicate in half of cases to test setAsset - NvBlastFamilySetAsset(family, assetDuplicate, messageLog); - NvBlastID id2 = NvBlastFamilyGetAssetID(family, messageLog); - EXPECT_TRUE(!memcmp(&assetID, &id2, sizeof(NvBlastID))); - } - - // actor - NvBlastActorDesc actorDesc; - actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - m_scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, m_scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - // Generate damage - std::set actors; - actors.insert(actor); - if (printActorCount) std::cout << "Actors: 1.. "; - for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) - { - GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); - blast(actors, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage); - if (printActorCount) std::cout << actors.size() << ".. "; - if (actors.size() > 0) - { - const NvBlastFamily* family = NvBlastActorGetFamily(*actors.begin(), messageLog); - const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog); - EXPECT_EQ((uint32_t)actors.size(), actorCount); - if ((uint32_t)actors.size() == actorCount) - { - std::vector buffer1(actorCount); - const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog); - EXPECT_EQ(actorsWritten, actorCount); - std::vector buffer2(actors.begin(), actors.end()); - EXPECT_EQ(0, memcmp(&buffer1[0], buffer2.data(), actorCount*sizeof(NvBlastActor*))); - } - } - // Test individual actors - if (actorTest != nullptr) - { - for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) - { - actorTest(*static_cast(*k), messageLog); - } - } - } - if (printActorCount) std::cout << "\n"; - - // Test fractured actor set - if (postDamageTest) - { - std::vector actorArray(actors.begin(), actors.end()); - postDamageTest(actorArray, messageLog); - } - - // Release remaining actors - for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) - { - NvBlastActorDeactivate(*k, messageLog); - } - actors.clear(); - - free(family); - } - - // Release asset data - free(asset); - free(assetDuplicate); - } - std::cout << "done.\n"; - } - - std::vector m_assets; - std::vector m_actors; - std::vector m_scratch; - static std::vector s_storage; - - static size_t s_curr; -}; - -// Static values -template -std::vector ActorTest::s_storage; - -template -size_t ActorTest::s_curr; - -// Specializations -typedef ActorTest ActorTestAllowWarnings; -typedef ActorTest ActorTestStrict; - -// Tests -TEST_F(ActorTestStrict, InstanceActors) -{ - // Build assets and instance actors - buildAssets(); - instanceActors(); - - // Release actors and destroy assets - releaseActors(); - destroyAssets(); -} - -TEST_F(ActorTestAllowWarnings, ActorHealthInitialization) -{ - // Test all assets - std::vector assetDescs; - assetDescs.insert(assetDescs.end(), g_assetDescs, g_assetDescs + getAssetDescCount()); - assetDescs.insert(assetDescs.end(), g_assetDescsMissingCoverage, g_assetDescsMissingCoverage + getAssetDescMissingCoverageCount()); - - struct TestMode - { - enum Enum - { - Uniform, - Nonuniform, - - Count - }; - }; - - for (auto assetDesc : assetDescs) - { - NvBlastAsset* asset = buildAsset(assetDesc); - EXPECT_TRUE(asset != nullptr); - - Nv::Blast::Asset& assetInt = static_cast(*asset); - - NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr); - - std::vector supportChunkHealths(graph.nodeCount); - for (size_t i = 0; i < supportChunkHealths.size(); ++i) - { - supportChunkHealths[i] = 1.0f + (float)i; - } - - std::vector bondHealths(assetInt.getBondCount()); - for (size_t i = 0; i < bondHealths.size(); ++i) - { - bondHealths[i] = 1.5f + (float)i; - } - - for (int chunkTestMode = 0; chunkTestMode < TestMode::Count; ++chunkTestMode) - { - for (int bondTestMode = 0; bondTestMode < TestMode::Count; ++bondTestMode) - { - NvBlastActorDesc actorDesc; - - switch (chunkTestMode) - { - default: - case TestMode::Uniform: - actorDesc.initialSupportChunkHealths = nullptr; - actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; - break; - case TestMode::Nonuniform: - actorDesc.initialSupportChunkHealths = supportChunkHealths.data(); - break; - } - - switch (bondTestMode) - { - default: - case TestMode::Uniform: - actorDesc.initialBondHealths = nullptr; - actorDesc.uniformInitialBondHealth = 2.0f; - break; - case TestMode::Nonuniform: - actorDesc.initialBondHealths = bondHealths.data(); - break; - } - - void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); - NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); - std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); - NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); - EXPECT_TRUE(actor != nullptr); - - Nv::Blast::Actor& actorInt = static_cast(*actor); - Nv::Blast::FamilyHeader* header = actorInt.getFamilyHeader(); - - - for (uint32_t i = 0; i < graph.nodeCount; ++i) - { - const uint32_t supportChunkIndex = graph.chunkIndices[i]; - for (Nv::Blast::Asset::DepthFirstIt it(assetInt, supportChunkIndex); (bool)it; ++it) - { - const uint32_t chunkIndex = (uint32_t)it; - const uint32_t lowerSupportIndex = assetInt.getContiguousLowerSupportIndex(chunkIndex); - NVBLAST_ASSERT(lowerSupportIndex < assetInt.getLowerSupportChunkCount()); - const float health = header->getLowerSupportChunkHealths()[lowerSupportIndex]; - switch (chunkTestMode) - { - default: - case TestMode::Uniform: - EXPECT_EQ(1.0f, health); - break; - case TestMode::Nonuniform: - EXPECT_EQ(supportChunkHealths[i], health); - break; - } - } - } - - for (uint32_t i = 0; i < assetInt.getBondCount(); ++i) - { - switch (bondTestMode) - { - default: - case TestMode::Uniform: - EXPECT_EQ(2.0f, header->getBondHealths()[i]); - break; - case TestMode::Nonuniform: - EXPECT_EQ(bondHealths[i], header->getBondHealths()[i]); - break; - } - } - - NvBlastActorDeactivate(actor, messageLog); - free(family); - } - } - - free(asset); - } -} - -TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, false); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, false); -} - -TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, true); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestCounts) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, true); -} - -TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); -} - -TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); -} - -TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestVisibility) -{ - partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorsTestVisibility) -{ - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorTestBlockSerialization) -{ - s_storage.resize(0); - damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockSerialize); - s_curr = 0; - damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockDeserialize); - s_storage.resize(0); -} - -TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationNewFamily) -{ - damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationNewFamily); -} - -TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationPartialBlock) -{ - damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationPartialBlock); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationNewFamily) -{ - damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationNewFamily); -} - -TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationPartialBlock) -{ - damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationPartialBlock); -} - -TEST_F(ActorTestStrict, DamageMultipleIslandLeafSupportActorsTestVisibility) -{ - typedef CubeAssetGenerator::BondFlags BF; - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Y_BONDS | BF::Z_BONDS); // Only connect y-z plane islands - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Z_BONDS); // Only connect z-direction islands - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::NO_BONDS); // All support chunks disconnected (single-chunk islands) -} - -TEST_F(ActorTestStrict, DamageBoundToWorldLeafSupportActorsTestVisibility) -{ - typedef CubeAssetGenerator::BondFlags BF; - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_MINUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Y_PLUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Z_MINUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS); - damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::X_MINUS_WORLD_BONDS - | BF::Y_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS - | BF::Z_PLUS_WORLD_BONDS | BF::Z_MINUS_WORLD_BONDS); -} +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "BlastBaseTest.h" +#include "AssetGenerator.h" + +#include +#include + +#include "NvBlastActor.h" +#include "NvBlastExtDamageShaders.h" + + +static bool chooseRandomGraphNodes(uint32_t* g, uint32_t count, const Nv::Blast::Actor& actor) +{ + const uint32_t graphNodeCount = actor.getGraphNodeCount(); + + if (graphNodeCount < count) + { + return false; + } + + std::vector graphNodeIndices(graphNodeCount); + uint32_t* index = graphNodeIndices.data(); + for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i ; ++i) + { + *index++ = (uint32_t)i; + } + struct UserDataSorter + { + UserDataSorter(const Nv::Blast::Actor& actor) : m_asset(*actor.getAsset()) {} + + bool operator () (uint32_t i0, uint32_t i1) const + { + const uint32_t c0 = m_asset.m_graph.getChunkIndices()[i0]; + const uint32_t c1 = m_asset.m_graph.getChunkIndices()[i1]; + if (Nv::Blast::isInvalidIndex(c0) || Nv::Blast::isInvalidIndex(c1)) + { + return c0 < c1; + } + return m_asset.getChunks()[c0].userData < m_asset.getChunks()[c1].userData; + } + + const Nv::Blast::Asset& m_asset; + } userDataSorter(actor); + std::sort(graphNodeIndices.data(), graphNodeIndices.data() + graphNodeCount, userDataSorter); + +#if 0 + std::vector descUserData(graphNodeCount); + for (uint32_t i = 0; i < graphNodeCount; ++i) + { + descUserData[i] = actor.getAsset()->m_chunks[actor.getAsset()->m_graph.m_chunkIndices[graphNodeIndices[i]]].userData; + } +#endif + + uint32_t t = 0; + uint32_t m = 0; + for (uint32_t i = 0; i < graphNodeCount && m < count; ++i, ++t) + { + NVBLAST_ASSERT(t < graphNodeCount); + if (t >= graphNodeCount) + { + break; + } + const float U = (float)rand()/RAND_MAX; // U is uniform random number in [0,1) + if ((graphNodeCount - t)*U < count - m) + { + g[m++] = graphNodeIndices[i]; + } + } + + return m == count; +} + + +static void blast(std::set& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage) +{ + std::vector chunkEvents; /* num lower-support chunks + bonds */ + std::vector bondEvents; /* num lower-support chunks + bonds */ + chunkEvents.resize(testAsset->solverChunks.size()); + bondEvents.resize(testAsset->solverBonds.size()); + + + std::vector splitScratch; + std::vector newActorsBuffer(testAsset->solverChunks.size()); + + NvBlastExtRadialDamageDesc damage = { + compressiveDamage, + { localPos.x, localPos.y, localPos.z }, + minRadius, + maxRadius + }; + + NvBlastExtProgramParams programParams = + { + &damage, + nullptr + }; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + size_t totalNewActorsCount = 0; + for (std::set::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();) + { + NvBlastActor* actor = *k; + NvBlastFractureBuffers events = { static_cast(bondEvents.size()), static_cast(chunkEvents.size()), bondEvents.data(), chunkEvents.data() }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr); + NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr); + const bool isDamaged = NvBlastActorIsSplitRequired(actor, nullptr); + bool removeActor = false; + + if (events.bondFractureCount + events.chunkFractureCount > 0) + { + NvBlastActorSplitEvent splitEvent; + splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount]; + uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount); + + splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr)); + const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr); + EXPECT_TRUE(isDamaged || newActorsCount == 0); + totalNewActorsCount += newActorsCount; + removeActor = splitEvent.deletedActor != NULL; + } + else + { + EXPECT_FALSE(isDamaged); + } + + if (removeActor) + { + k = actorsToDamage.erase(k); + } + else + { + ++k; + } + } + + for (size_t i = 0; i < totalNewActorsCount; ++i) + { + actorsToDamage.insert(newActorsBuffer[i]); + } +} + + +template +class ActorTest : public BlastBaseTest +{ +public: + ActorTest() + { + + } + + static void messageLog(int type, const char* msg, const char* file, int line) + { + BlastBaseTest::messageLog(type, msg, file, line); + } + + static void* alloc(size_t size) + { + return BlastBaseTest::alignedZeroedAlloc(size); + } + + static void free(void* mem) + { + BlastBaseTest::alignedFree(mem); + } + + NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc) + { + // fix desc if wrong order or missing coverage first + NvBlastAssetDesc fixedDesc = desc; + std::vector chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount); + std::vector bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount); + std::vector chunkReorderMap(desc.chunkCount); + std::vector scratch(desc.chunkCount * sizeof(NvBlastChunkDesc)); + NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog); + NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog); + fixedDesc.chunkDescs = chunkDescs.data(); + fixedDesc.bondDescs = bondDescs.empty() ? nullptr : bondDescs.data(); + + // create asset + m_scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&fixedDesc, messageLog)); + void* mem = alloc(NvBlastGetAssetMemorySize(&fixedDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &fixedDesc, m_scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + return asset; + } + + void buildAssets() + { + m_assets.resize(getAssetDescCount()); + for (uint32_t i = 0; i < m_assets.size(); ++i) + { + m_assets[i] = buildAsset(g_assetDescs[i]); + } + } + + NvBlastActor* instanceActor(const NvBlastAsset& asset) + { + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(&asset, nullptr)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, &asset, nullptr); + std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + return actor; + } + + void instanceActors() + { + m_actors.resize(m_assets.size()); + for (uint32_t i = 0; i < m_actors.size(); ++i) + { + m_actors[i] = instanceActor(*m_assets[i]); + } + } + + void releaseActors() + { + for (uint32_t i = 0; i < m_actors.size(); ++i) + { + NvBlastFamily* family = NvBlastActorGetFamily(m_actors[i], messageLog); + + const bool actorReleaseResult = NvBlastActorDeactivate(m_actors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + + free(family); + } + } + + void destroyAssets() + { + for (uint32_t i = 0; i < m_assets.size(); ++i) + { + free(m_assets[i]); + } + } + + void instanceAndPartitionRecursively + ( + const NvBlastAsset& asset, + bool partitionToSubsupport, + void (*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), + void (*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool) + ) + { + const Nv::Blast::Asset& solverAsset = *static_cast(&asset); + + std::vector actors; + std::vector buffer(NvBlastAssetGetChunkCount(&asset, messageLog)); + + // Instance the first actor from the asset + actors.push_back(static_cast(instanceActor(asset))); + + NvBlastFamily* family = NvBlastActorGetFamily(actors[0], messageLog); + + const uint32_t supportChunkCount = NvBlastAssetGetSupportChunkCount(&asset, messageLog); + const uint32_t leafChunkCount = actors[0]->getAsset()->m_leafChunkCount; + + // Now randomly partition the actors in the array, and keep going until we're down to single support chunks + bool canFracture = true; + + while (canFracture) + { + canFracture = false; + + for (uint32_t actorToPartition = 0; actorToPartition < actors.size(); ++actorToPartition) + { + Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[actorToPartition]; + if (a == nullptr) + { + continue; + } + + m_scratch.reserve((size_t)NvBlastActorGetRequiredScratchForSplit(a, messageLog)); + + if (preSplitTest) + { + preSplitTest(*a, nullptr); + } + + const bool singleLowerSupportChunk = a->getGraphNodeCount() <= 1; + uint32_t newActorCount = 0; + + for (int damageNum = 0; newActorCount < 2 && damageNum < 100; ++damageNum) // Avoid infinite loops + { + if (!singleLowerSupportChunk) + { + uint32_t g[2]; + chooseRandomGraphNodes(g, 2, *a); + const uint32_t bondIndex = solverAsset.m_graph.findBond(g[0], g[1]); + if (bondIndex != Nv::Blast::invalidIndex()) + { + a->damageBond(g[0], g[1], bondIndex, 100.0f); + a->findIslands(m_scratch.data()); + } + } + else + if (!partitionToSubsupport) + { + continue; + } + + // Split actor + newActorCount = a->partition((Nv::Blast::Actor**)&buffer[0], (uint32_t)buffer.size(), messageLog); + + if (newActorCount >= 2) + { + actors[actorToPartition] = nullptr; + } + } + + if (newActorCount > 1) + { + canFracture = true; + } + + for (uint32_t i = 0; i < newActorCount; ++i) + { + actors.push_back(buffer[i]); + buffer[i]->updateVisibleChunksFromGraphNodes(); + } + } + } + + if (postSplitTest) + { + postSplitTest(actors, leafChunkCount, supportChunkCount, partitionToSubsupport); + } + + for (auto actor : actors) + { + if (actor) + actor->release(); + } + + free(family); + } + + static void recursivePartitionPostSplitTestCounts(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) + { + // Test to see that all actors are split down to single support chunks + uint32_t remainingActorCount = 0; + for (uint32_t i = 0; i < actors.size(); ++i) + { + Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; + if (a == nullptr) + { + continue; + } + + ++remainingActorCount; + + NVBLAST_ASSERT(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); + EXPECT_TRUE(1 == a->getVisibleChunkCount() || a->isBoundToWorld()); + if (!partitionToSubsupport) + { + EXPECT_EQ(1, a->getGraphNodeCount()); + } + + if (0 == a->getVisibleChunkCount()) + { + EXPECT_TRUE(a->isBoundToWorld()); + EXPECT_EQ(1, a->getGraphNodeCount()); + EXPECT_EQ(a->getFamilyHeader()->m_asset->m_graph.m_nodeCount - 1, a->getFirstGraphNodeIndex()); + --remainingActorCount; // Do not count this as a remaining actor, to be compared with leaf or support chunk counts later + } + + const bool actorReleaseResult = NvBlastActorDeactivate(actors[i], nullptr); + EXPECT_TRUE(actorReleaseResult); + } + + if (partitionToSubsupport) + { + EXPECT_EQ(leafChunkCount, remainingActorCount); + } + else + { + EXPECT_EQ(supportChunkCount, remainingActorCount); + } + } + + static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog) + { + const Nv::Blast::Asset& asset = *actor.getAsset(); + const NvBlastChunk* chunks = asset.getChunks(); + + if (actor.isSubSupportChunk()) + { + EXPECT_EQ(1, actor.getVisibleChunkCount()); + + const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor); + + EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex); + + // Make sure the visible chunk is subsupport + // Array of support flags + std::vector isSupport(asset.m_chunkCount, false); + for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i) + { + const uint32_t chunkIndex = asset.m_graph.getChunkIndices()[i]; + if (!Nv::Blast::isInvalidIndex(chunkIndex)) + { + isSupport[chunkIndex] = true; + } + } + + // Climb hierarchy to find support chunk + uint32_t chunkIndex = firstVisibleChunkIndex; + while (chunkIndex != Nv::Blast::invalidIndex()) + { + if (isSupport[chunkIndex]) + { + break; + } + chunkIndex = chunks[chunkIndex].parentChunkIndex; + } + + EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex)); + } + else + { + // Array of visibility flags + std::vector isVisible(asset.m_chunkCount, false); + for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i) + { + isVisible[(uint32_t)i] = true; + } + + // Mark visible nodes representing graph chunks + std::vector visibleChunkFound(asset.m_chunkCount, false); + + // Make sure every graph chunk is represented by a visible chunk, or represents the world + for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i) + { + const uint32_t graphNodeIndex = (uint32_t)i; + uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex]; + // Climb hierarchy to find visible chunk + while (chunkIndex != Nv::Blast::invalidIndex()) + { + // Check that chunk owners are accurate + EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]); + if (isVisible[chunkIndex]) + { + visibleChunkFound[chunkIndex] = true; + break; + } + chunkIndex = chunks[chunkIndex].parentChunkIndex; + } + EXPECT_TRUE(!Nv::Blast::isInvalidIndex(chunkIndex) || (graphNodeIndex == asset.m_graph.m_nodeCount-1 && actor.isBoundToWorld())); + } + + // Check that all visible chunks are accounted for + for (uint32_t i = 0; i < asset.m_chunkCount; ++i) + { + EXPECT_EQ(visibleChunkFound[i], isVisible[i]); + } + + // Make sure that, if all siblings are intact, they are invisible + for (uint32_t i = 0; i < asset.m_chunkCount; ++i) + { + bool allIntact = true; + bool noneVisible = true; + if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport + { + for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j) + { + allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex(); + noneVisible = noneVisible && !isVisible[j]; + } + EXPECT_TRUE(!allIntact || noneVisible); + } + } + } + } + + static void recursivePartitionPostSplitTestVisibleChunks(const std::vector& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport) + { + for (uint32_t i = 0; i < actors.size(); ++i) + { + Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i]; + if (a == nullptr) + { + continue; + } + + testActorVisibleChunks(*a, nullptr); + } + } + + void partitionActorsToSupportChunks + ( + uint32_t assetDescCount, + const NvBlastAssetDesc* assetDescs, + void(*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog), + void(*postSplitTest)(const std::vector&, uint32_t, uint32_t, bool), + bool partitionToSubsupport + ) + { + srand(0); + + for (uint32_t i = 0; i < assetDescCount; ++i) + { + // Create an asset + NvBlastAsset* asset = buildAsset(assetDescs[i]); + + // Perform repeated partitioning + instanceAndPartitionRecursively(*asset, partitionToSubsupport, preSplitTest, postSplitTest); + + // Free the asset + free(asset); + } + } + + static void compareFamilies(const NvBlastFamily* family1, const NvBlastFamily* family2, size_t size, NvBlastLog logFn) + { + const char* block1 = reinterpret_cast(family1); + const char* block2 = reinterpret_cast(family2); +#if 0 + EXPECT_EQ(0, memcmp(block1, block2, size)); +#else + bool diffFound = false; + size_t startDiff = 0; + for (size_t i = 0; i < size; ++i) + { + if (block1[i] != block2[i]) + { + diffFound = true; + startDiff = i; + break; + } + } + if (!diffFound) + { + return; + } + size_t endDiff = startDiff; + for (size_t i = size; i--;) + { + if (block1[i] != block2[i]) + { + endDiff = i; + break; + } + } + std::ostringstream msg; + msg << "Block deserialization does not match current block in position range [" << startDiff << ", " << endDiff << "]."; + logFn(NvBlastMessage::Error, msg.str().c_str(), __FILE__, __LINE__); +#endif + } + + static void testActorBlockSerialize(std::vector& actors, NvBlastLog logFn) + { + if (actors.size()) + { + const NvBlastFamily* family = NvBlastActorGetFamily(actors[0], logFn); + const uint32_t size = NvBlastFamilyGetSize(family, logFn); + s_storage.insert(s_storage.end(), (char*)family, (char*)family + size); + } + } + + static void testActorBlockDeserialize(std::vector& actors, NvBlastLog logFn) + { + if (actors.size()) + { + EXPECT_LT(s_curr, s_storage.size()); + const NvBlastFamily* family = reinterpret_cast(&s_storage[s_curr]); + const uint32_t size = NvBlastFamilyGetSize(family, logFn); + EXPECT_LE(s_curr + size, s_storage.size()); + s_curr += size; + const NvBlastFamily* actorFamily = NvBlastActorGetFamily(actors[0], logFn); + // Family may contain different assets pointers, copy into new family block and set the same asset before comparing + Nv::Blast::Actor& a = *static_cast(actors[0]); + const Nv::Blast::Asset* solverAsset = a.getAsset(); + std::vector storageFamilyCopy((char*)family, (char*)family + size); + NvBlastFamily* storageFamily = reinterpret_cast(storageFamilyCopy.data()); + NvBlastFamilySetAsset(storageFamily, solverAsset, logFn); + { + const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn); + std::vector blockActors(actorCountExpected); + const uint32_t actorCountReturned = NvBlastFamilyGetActors(blockActors.data(), actorCountExpected, storageFamily, logFn); + EXPECT_EQ(actorCountExpected, actorCountReturned); + } + compareFamilies(storageFamily, actorFamily, size, logFn); + } + } + + // Serialize all actors and then deserialize back into a new family in a random order, and compare with the original family + static void testActorSerializationNewFamily(std::vector& actors, NvBlastLog logFn) + { + if (actors.size() == 0) + { + return; + } + + Nv::Blast::Actor& a = *static_cast(actors[0]); + const Nv::Blast::Asset* solverAsset = a.getAsset(); + + const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); + + std::vector< std::vector > streams(actors.size()); + for (size_t i = 0; i < actors.size(); ++i) + { + const uint32_t serSize = NvBlastActorGetSerializationSize(actors[i], logFn); + EXPECT_GE(serSizeBound, serSize); + std::vector& stream = streams[i]; + stream.resize(serSize); + const uint32_t bytesWritten = NvBlastActorSerialize(stream.data(), serSize, actors[i], logFn); + EXPECT_EQ(serSize, bytesWritten); + } + + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(solverAsset, logFn)); + NvBlastFamily* newFamily = NvBlastAssetCreateFamily(fmem, solverAsset, logFn); + + std::vector order(actors.size()); + for (size_t i = 0; i < order.size(); ++i) + { + order[i] = i; + } + std::random_shuffle(order.begin(), order.end()); + + for (size_t i = 0; i < actors.size(); ++i) + { + NvBlastActor* newActor = NvBlastFamilyDeserializeActor(newFamily, streams[order[i]].data(), logFn); + EXPECT_TRUE(newActor != nullptr); + } + + const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); + compareFamilies(oldFamily, newFamily, NvBlastFamilyGetSize(oldFamily, logFn), logFn); + + free(newFamily); + } + + // Copy the family and then serialize some subset of actors, deleting them afterwards. + // Then, deserialize back into the block and compare the original and new families. + static void testActorSerializationPartialBlock(std::vector& actors, NvBlastLog logFn) + { + if (actors.size() <= 1) + { + return; + } + + Nv::Blast::Actor& a = *static_cast(actors[0]); + const Nv::Blast::Asset* solverAsset = a.getAsset(); + + const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn); + const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn); + std::vector buffer((char*)oldFamily, (char*)oldFamily + size); + NvBlastFamily* familyCopy = reinterpret_cast(buffer.data()); + + const uint32_t serCount = 1 + (rand() % actors.size() - 1); + + const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn); + std::vector actorsRemaining(actorCount); + const uint32_t actorsInFamily = NvBlastFamilyGetActors(&actorsRemaining[0], actorCount, familyCopy, logFn); + EXPECT_EQ(actorCount, actorsInFamily); + + const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn); + + std::vector< std::vector > streams(serCount); + for (uint32_t i = 0; i < serCount; ++i) + { + std::vector& stream = streams[i]; + const uint32_t indexToStream = rand() % actorsRemaining.size(); + NvBlastActor* actorToStream = actorsRemaining[indexToStream]; + std::swap(actorsRemaining[indexToStream], actorsRemaining[actorsRemaining.size() - 1]); + actorsRemaining.pop_back(); + const uint32_t serSize = NvBlastActorGetSerializationSize(actorToStream, logFn); + EXPECT_GE(serSizeBound, serSize); + stream.resize(serSize); + const uint32_t bytesWritten = NvBlastActorSerialize(&stream[0], serSize, actorToStream, logFn); + EXPECT_EQ(serSize, bytesWritten); + NvBlastActorDeactivate(actorToStream, logFn); + } + + for (uint32_t i = 0; i < serCount; ++i) + { + NvBlastActor* newActor = NvBlastFamilyDeserializeActor(familyCopy, streams[i].data(), logFn); + EXPECT_TRUE(newActor != nullptr); + } + + compareFamilies(oldFamily, familyCopy, size, logFn); + } + + void damageLeafSupportActors + ( + uint32_t assetCount, + uint32_t familyCount, + uint32_t damageCount, + bool simple, + void (*actorTest)(const Nv::Blast::Actor&, NvBlastLog), + void (*postDamageTest)(std::vector&, NvBlastLog), + CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::BondFlags::ALL_INTERNAL_BONDS + ) + { + const float relativeDamageRadius = simple ? 0.75f : 0.2f; + const float compressiveDamage = 1.0f; + const uint32_t minChunkCount = simple ? 9 : 100; + const uint32_t maxChunkCount = simple ? 9 : 10000; + const bool printActorCount = false; + + srand(0); + + std::cout << "Asset # (out of " << assetCount << "): "; + for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum) + { + std::cout << assetNum + 1 << ".. "; + CubeAssetGenerator::Settings settings; + settings.extents = GeneratorAsset::Vec3(1, 1, 1); + settings.bondFlags = bondFlags; + CubeAssetGenerator::DepthInfo depthInfo; + depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1); + depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags; + settings.depths.push_back(depthInfo); + uint32_t chunkCount = 1; + while (chunkCount < minChunkCount) + { + uint32_t chunkMul; + do + { + depthInfo.slicesPerAxis = simple ? GeneratorAsset::Vec3(2, 2, 2) : GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4)); + chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z); + } while (chunkMul == 1); + if (chunkCount*chunkMul > maxChunkCount) + { + break; + } + chunkCount *= chunkMul; + settings.depths.push_back(depthInfo); + settings.extents = settings.extents * depthInfo.slicesPerAxis; + } + settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support + + // Make largest direction unit size + settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z))); + + // Create asset + GeneratorAsset testAsset; + CubeAssetGenerator::generate(testAsset, settings); + + NvBlastAssetDesc desc; + desc.chunkDescs = testAsset.solverChunks.data(); + desc.chunkCount = (uint32_t)testAsset.solverChunks.size(); + desc.bondDescs = testAsset.solverBonds.data(); + desc.bondCount = (uint32_t)testAsset.solverBonds.size(); + NvBlastAsset* asset = buildAsset(desc); + NvBlastID assetID = NvBlastAssetGetID(asset, messageLog); + + // copy asset (for setAsset testing) + const char* data = (const char*)asset; + const uint32_t dataSize = NvBlastAssetGetSize(asset, messageLog); + char* duplicateData = (char*)alloc(dataSize); + memcpy(duplicateData, data, dataSize); + NvBlastAsset* assetDuplicate = (NvBlastAsset*)duplicateData; + + // Generate families + for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum) + { + // family + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); // Using zeroingAlloc in case actorTest compares memory blocks + NvBlastID id = NvBlastFamilyGetAssetID(family, messageLog); + EXPECT_TRUE(!memcmp(&assetID, &id, sizeof(NvBlastID))); + if (rand() % 2 == 0) + { + // replace asset with duplicate in half of cases to test setAsset + NvBlastFamilySetAsset(family, assetDuplicate, messageLog); + NvBlastID id2 = NvBlastFamilyGetAssetID(family, messageLog); + EXPECT_TRUE(!memcmp(&assetID, &id2, sizeof(NvBlastID))); + } + + // actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + m_scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, m_scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // Generate damage + std::set actors; + actors.insert(actor); + if (printActorCount) std::cout << "Actors: 1.. "; + for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum) + { + GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f); + blast(actors, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage); + if (printActorCount) std::cout << actors.size() << ".. "; + if (actors.size() > 0) + { + const NvBlastFamily* family = NvBlastActorGetFamily(*actors.begin(), messageLog); + const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog); + EXPECT_EQ((uint32_t)actors.size(), actorCount); + if ((uint32_t)actors.size() == actorCount) + { + std::vector buffer1(actorCount); + const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog); + EXPECT_EQ(actorsWritten, actorCount); + std::vector buffer2(actors.begin(), actors.end()); + EXPECT_EQ(0, memcmp(&buffer1[0], buffer2.data(), actorCount*sizeof(NvBlastActor*))); + } + } + // Test individual actors + if (actorTest != nullptr) + { + for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) + { + actorTest(*static_cast(*k), messageLog); + } + } + } + if (printActorCount) std::cout << "\n"; + + // Test fractured actor set + if (postDamageTest) + { + std::vector actorArray(actors.begin(), actors.end()); + postDamageTest(actorArray, messageLog); + } + + // Release remaining actors + for (std::set::iterator k = actors.begin(); k != actors.end(); ++k) + { + NvBlastActorDeactivate(*k, messageLog); + } + actors.clear(); + + free(family); + } + + // Release asset data + free(asset); + free(assetDuplicate); + } + std::cout << "done.\n"; + } + + std::vector m_assets; + std::vector m_actors; + std::vector m_scratch; + static std::vector s_storage; + + static size_t s_curr; +}; + +// Static values +template +std::vector ActorTest::s_storage; + +template +size_t ActorTest::s_curr; + +// Specializations +typedef ActorTest ActorTestAllowWarnings; +typedef ActorTest ActorTestStrict; + +// Tests +TEST_F(ActorTestStrict, InstanceActors) +{ + // Build assets and instance actors + buildAssets(); + instanceActors(); + + // Release actors and destroy assets + releaseActors(); + destroyAssets(); +} + +TEST_F(ActorTestAllowWarnings, ActorHealthInitialization) +{ + // Test all assets + std::vector assetDescs; + assetDescs.insert(assetDescs.end(), g_assetDescs, g_assetDescs + getAssetDescCount()); + assetDescs.insert(assetDescs.end(), g_assetDescsMissingCoverage, g_assetDescsMissingCoverage + getAssetDescMissingCoverageCount()); + + struct TestMode + { + enum Enum + { + Uniform, + Nonuniform, + + Count + }; + }; + + for (auto assetDesc : assetDescs) + { + NvBlastAsset* asset = buildAsset(assetDesc); + EXPECT_TRUE(asset != nullptr); + + Nv::Blast::Asset& assetInt = static_cast(*asset); + + NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr); + + std::vector supportChunkHealths(graph.nodeCount); + for (size_t i = 0; i < supportChunkHealths.size(); ++i) + { + supportChunkHealths[i] = 1.0f + (float)i; + } + + std::vector bondHealths(assetInt.getBondCount()); + for (size_t i = 0; i < bondHealths.size(); ++i) + { + bondHealths[i] = 1.5f + (float)i; + } + + for (int chunkTestMode = 0; chunkTestMode < TestMode::Count; ++chunkTestMode) + { + for (int bondTestMode = 0; bondTestMode < TestMode::Count; ++bondTestMode) + { + NvBlastActorDesc actorDesc; + + switch (chunkTestMode) + { + default: + case TestMode::Uniform: + actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + break; + case TestMode::Nonuniform: + actorDesc.initialSupportChunkHealths = supportChunkHealths.data(); + break; + } + + switch (bondTestMode) + { + default: + case TestMode::Uniform: + actorDesc.initialBondHealths = nullptr; + actorDesc.uniformInitialBondHealth = 2.0f; + break; + case TestMode::Nonuniform: + actorDesc.initialBondHealths = bondHealths.data(); + break; + } + + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr); + std::vector scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + Nv::Blast::Actor& actorInt = static_cast(*actor); + Nv::Blast::FamilyHeader* header = actorInt.getFamilyHeader(); + + + for (uint32_t i = 0; i < graph.nodeCount; ++i) + { + const uint32_t supportChunkIndex = graph.chunkIndices[i]; + for (Nv::Blast::Asset::DepthFirstIt it(assetInt, supportChunkIndex); (bool)it; ++it) + { + const uint32_t chunkIndex = (uint32_t)it; + const uint32_t lowerSupportIndex = assetInt.getContiguousLowerSupportIndex(chunkIndex); + NVBLAST_ASSERT(lowerSupportIndex < assetInt.getLowerSupportChunkCount()); + const float health = header->getLowerSupportChunkHealths()[lowerSupportIndex]; + switch (chunkTestMode) + { + default: + case TestMode::Uniform: + EXPECT_EQ(1.0f, health); + break; + case TestMode::Nonuniform: + EXPECT_EQ(supportChunkHealths[i], health); + break; + } + } + } + + for (uint32_t i = 0; i < assetInt.getBondCount(); ++i) + { + switch (bondTestMode) + { + default: + case TestMode::Uniform: + EXPECT_EQ(2.0f, header->getBondHealths()[i]); + break; + case TestMode::Nonuniform: + EXPECT_EQ(bondHealths[i], header->getBondHealths()[i]); + break; + } + } + + NvBlastActorDeactivate(actor, messageLog); + free(family); + } + } + + free(asset); + } +} + +TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, false); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, false); +} + +TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, true); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestCounts) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, true); +} + +TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false); +} + +TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); +} + +TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestVisibility) +{ + partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorsTestVisibility) +{ + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorTestBlockSerialization) +{ + s_storage.resize(0); + damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockSerialize); + s_curr = 0; + damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockDeserialize); + s_storage.resize(0); +} + +TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationNewFamily) +{ + damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationNewFamily); +} + +TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationPartialBlock) +{ + damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationPartialBlock); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationNewFamily) +{ + damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationNewFamily); +} + +TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationPartialBlock) +{ + damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationPartialBlock); +} + +TEST_F(ActorTestStrict, DamageMultipleIslandLeafSupportActorsTestVisibility) +{ + typedef CubeAssetGenerator::BondFlags BF; + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Y_BONDS | BF::Z_BONDS); // Only connect y-z plane islands + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Z_BONDS); // Only connect z-direction islands + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::NO_BONDS); // All support chunks disconnected (single-chunk islands) +} + +TEST_F(ActorTestStrict, DamageBoundToWorldLeafSupportActorsTestVisibility) +{ + typedef CubeAssetGenerator::BondFlags BF; + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_MINUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Y_PLUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Z_MINUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS); + damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::X_MINUS_WORLD_BONDS + | BF::Y_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS + | BF::Z_PLUS_WORLD_BONDS | BF::Z_MINUS_WORLD_BONDS); +} -- cgit v1.2.3