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/TkTests.cpp | 3214 ++++++++++++++++++++++----------------------- 1 file changed, 1607 insertions(+), 1607 deletions(-) mode change 100644 => 100755 test/src/unit/TkTests.cpp (limited to 'test/src/unit/TkTests.cpp') diff --git a/test/src/unit/TkTests.cpp b/test/src/unit/TkTests.cpp old mode 100644 new mode 100755 index e07d4a0..9687c68 --- a/test/src/unit/TkTests.cpp +++ b/test/src/unit/TkTests.cpp @@ -1,1607 +1,1607 @@ -// This code contains NVIDIA Confidential Information and is disclosed to you -// under a form of NVIDIA software license agreement provided separately to you. -// -// Notice -// NVIDIA Corporation and its licensors retain all intellectual property and -// proprietary rights in and to this software and related documentation and -// any modifications thereto. Any use, reproduction, disclosure, or -// distribution of this software and related documentation without an express -// license agreement from NVIDIA Corporation is strictly prohibited. -// -// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES -// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO -// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, -// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. -// -// Information and code furnished is believed to be accurate and reliable. -// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such -// information or for any infringement of patents or other rights of third parties that may -// result from its use. No license is granted by implication or otherwise under any patent -// or patent rights of NVIDIA Corporation. Details are subject to change without notice. -// This code supersedes and replaces all information previously supplied. -// NVIDIA Corporation products are not authorized for use as critical -// components in life support devices or systems without express written approval of -// NVIDIA Corporation. -// -// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. - - -#include "TkBaseTest.h" - -#include -#include -#include - -#include "PsMemoryBuffer.h" - -#include "NvBlastTime.h" - -#include "NvBlastExtPxTask.h" - -struct ExpectedVisibleChunks -{ - ExpectedVisibleChunks() :numActors(0), numChunks(0) {} - ExpectedVisibleChunks(size_t a, size_t c) :numActors(a), numChunks(c) {} - size_t numActors; size_t numChunks; -}; - -void testResults(std::vector& families, std::map& expectedVisibleChunks) -{ - size_t numActors = 0; - for (TkFamily* fam : families) - { - auto ex = expectedVisibleChunks[fam]; - EXPECT_EQ(ex.numActors, fam->getActorCount()); - numActors += ex.numActors; - std::vector actors(fam->getActorCount()); - fam->getActors(actors.data(), static_cast(actors.size())); - for (TkActor* actor : actors) - { - EXPECT_EQ(ex.numChunks, actor->getVisibleChunkCount()); - } - } - - size_t numActorsExpected = 0; - for (auto expected : expectedVisibleChunks) - { - numActorsExpected += expected.second.numActors; - } - - EXPECT_EQ(numActorsExpected, numActors); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Tests -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -TEST_F(TkTestStrict, CreateFramework) -{ - createFramework(); - releaseFramework(); -} - -TEST_F(TkTestStrict, CreateAsset) -{ - createFramework(); - - createTestAssets(); - releaseTestAssets(); - - releaseFramework(); -} - -#if USE_PHYSX_DISPATCHER -TEST_F(TkTestStrict, DISABLED_MemLeak) -{ - PxFoundation* pxFoundation = PxCreateFoundation(PX_FOUNDATION_VERSION, NvBlastGetPxAllocatorCallback(), NvBlastGetPxErrorCallback()); - PxU32 affinity[] = { 1, 2, 4, 8 }; - PxDefaultCpuDispatcher* cpuDispatcher = PxDefaultCpuDispatcherCreate(4, affinity); - cpuDispatcher->setRunProfiled(false); - PxTaskManager* taskman = PxTaskManager::createTaskManager(NvBlastGetPxErrorCallback(), cpuDispatcher, nullptr); - - cpuDispatcher->release(); - taskman->release(); - pxFoundation->release(); -} -#endif - -TEST_F(TkTestStrict, ActorDamageNoGroup) -{ - createFramework(); - createTestAssets(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - TkActorDesc actorDesc; - actorDesc.asset = testAssets[0]; - TkActor* actor = fwk->createActor(actorDesc); - - const size_t bondFractureCount = 4; - NvBlastFractureBuffers commands; - NvBlastBondFractureData bdata[bondFractureCount]; - for (uint32_t i = 0; i < bondFractureCount; i++) - { - bdata[i].nodeIndex0 = 2 * i + 0; - bdata[i].nodeIndex1 = 2 * i + 1; - bdata[i].health = 1.0f; - } - commands.bondFractureCount = bondFractureCount; - commands.bondFractures = bdata; - commands.chunkFractureCount = 0; - commands.chunkFractures = nullptr; - actor->applyFracture(&commands, &commands); - - TkFamily& family = actor->getFamily(); - - EXPECT_TRUE(commands.bondFractureCount == 4); - EXPECT_TRUE(actor->isPending()); - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - group->addActor(*actor); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_FALSE(actor->isPending()); - EXPECT_EQ(2, family.getActorCount()); - - releaseFramework(); -} - -TEST_F(TkTestStrict, ActorDamageGroup) -{ - TEST_ZONE_BEGIN("ActorDamageGroup"); - - createFramework(); - createTestAssets(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - TestFamilyTracker ftrack1, ftrack2; - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - - NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); - NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; - - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - std::vector families; - TkFamily* trackedFamily; - std::map expectedVisibleChunks; - - { - TkActorDesc adesc(testAssets[0]); - - TkActor* actor1 = fwk->createActor(adesc); - EXPECT_TRUE(actor1 != nullptr); - - TkActor* actor2 = fwk->createActor(adesc); - EXPECT_TRUE(actor2 != nullptr); - - - expectedVisibleChunks[&actor1->getFamily()] = ExpectedVisibleChunks(8, 1); // full damage - expectedVisibleChunks[&actor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split - - GeneratorAsset cube; - TkAssetDesc assetDesc; - generateCube(cube, assetDesc, 5, 2); - assetDesc.bondFlags = nullptr; - - TkAsset* cubeAsset = fwk->createAsset(assetDesc); - testAssets.push_back(cubeAsset); - - TkActorDesc cubeAD(cubeAsset); - - TkActor* cubeActor1 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor1 != nullptr); - - trackedFamily = &cubeActor1->getFamily(); - cubeActor1->getFamily().addListener(ftrack1); - - TkActor* cubeActor2 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor2 != nullptr); - - expectedVisibleChunks[&cubeActor1->getFamily()] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each - expectedVisibleChunks[&cubeActor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split - - ftrack1.insertActor(cubeActor1); - ftrack2.insertActor(actor1); - - actor1->getFamily().addListener(ftrack2); - - TEST_ZONE_BEGIN("add to groups"); - group->addActor(*cubeActor1); - group->addActor(*cubeActor2); - group->addActor(*actor1); - group->addActor(*actor2); - TEST_ZONE_END("add to groups"); - - families.push_back(&cubeActor1->getFamily()); - families.push_back(&cubeActor2->getFamily()); - families.push_back(&actor1->getFamily()); - families.push_back(&actor2->getFamily()); - - cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); - actor1->damage(getFalloffProgram(), &radialDamageParams); - } - - EXPECT_FALSE(group->endProcess()); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - for (TkActor* actor : actors) - { - actor->damage(getCubeSlicerProgram(), &csDamageParams1); - } - } - expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4, 2); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - for (TkActor* actor : actors) - { - actor->damage(getCubeSlicerProgram(), &csDamageParams2); - } - } - - expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(8, 1); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - TEST_ZONE_BEGIN("damage"); - for (TkActor* actor : actors) - { - actor->damage(getFalloffProgram(), &radialDamageParams); - } - TEST_ZONE_END("damage"); - } - expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4096, 1); - - m_groupTM->process(); - m_groupTM->wait(); - - testResults(families, expectedVisibleChunks); - - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - TEST_ZONE_BEGIN("damage"); - for (TkActor* actor : actors) - { - actor->damage(getShearProgram(), &shearDamageParams); - } - TEST_ZONE_END("damage"); - } - - m_groupTM->process(); - m_groupTM->wait(); - - - - { - std::vector actors(trackedFamily->getActorCount()); - trackedFamily->getActors(actors.data(), static_cast(actors.size())); - TEST_ZONE_BEGIN("damage"); - for (TkActor* actor : actors) - { - actor->damage(getShearProgram(), &shearDamageParams); - } - TEST_ZONE_END("damage"); - } - - m_groupTM->process(); - m_groupTM->wait(); - - group->release(); - - TEST_ZONE_BEGIN("family release"); - trackedFamily->release(); - TEST_ZONE_END("family release"); - - releaseTestAssets(); - releaseFramework(); - - TEST_ZONE_END("ActorDamageGroup"); -} - - -TEST_F(TkTestStrict, ActorDamageMultiGroup) -{ - createFramework(); - createTestAssets(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - TestFamilyTracker ftrack1, ftrack2; - - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group0 = fwk->createGroup(gdesc); - EXPECT_TRUE(group0 != nullptr); - TkGroup* group1 = fwk->createGroup(gdesc); - EXPECT_TRUE(group1 != nullptr); - - ExtGroupTaskManager& gtm1 = *ExtGroupTaskManager::create(*m_taskman, group1); - ExtGroupTaskManager& gtm0 = *ExtGroupTaskManager::create(*m_taskman, group0); - - std::vector families(2); - std::map expectedVisibleChunks; - - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - // prepare 2 equal actors/families and damage - { - GeneratorAsset cube; - TkAssetDesc assetDesc; - generateCube(cube, assetDesc, 6, 2, 5); - assetDesc.bondFlags = nullptr; - - TkAsset* cubeAsset = fwk->createAsset(assetDesc); - testAssets.push_back(cubeAsset); - - TkActorDesc cubeAD(cubeAsset); - - TkActor* cubeActor0 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor0 != nullptr); - cubeActor0->getFamily().addListener(ftrack1); - - TkActor* cubeActor1 = fwk->createActor(cubeAD); - EXPECT_TRUE(cubeActor1 != nullptr); - cubeActor1->getFamily().addListener(ftrack2); - - ftrack1.insertActor(cubeActor0); - ftrack2.insertActor(cubeActor1); - - group0->addActor(*cubeActor0); - group1->addActor(*cubeActor1); - - families[0] = (&cubeActor0->getFamily()); - families[1] = (&cubeActor1->getFamily()); - - { - cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams0); - cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams1); - - cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); - } - - expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(4, 2); // split in 4, 2 chunks each - expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each - } - - // async process 2 groups - { - EXPECT_GT(gtm0.process(2), (uint32_t)0); - EXPECT_GT(gtm1.process(2), (uint32_t)0); - uint32_t completed = 0; - while (completed < 2) - { - if (gtm0.wait(false)) - completed++; - if (gtm1.wait(false)) - completed++; - } - } - - // checks - testResults(families, expectedVisibleChunks); - EXPECT_EQ(families[0]->getActorCount(), 4); - EXPECT_EQ(group0->getActorCount(), 4); - EXPECT_EQ(families[1]->getActorCount(), 2); - EXPECT_EQ(group1->getActorCount(), 2); - - // we have group0 with 4 actors 2 chunks: - // group0: [2]' [2]' [2]' [2]' (family0') - // group1: [4]'' [4]'' (family1'') - // rearrange: - // group0: [2]' [2]' [4]'' - // group1: [4]'' [2]' [2]' - { - TkActor* group0Actors[2]; - group0->getActors(group0Actors, 2, 1); // start index: 1, because..why not? - TkActor* group1Actors[2]; - group1->getActors(group1Actors, 2, 0); - group0Actors[0]->removeFromGroup(); - group1->addActor(*group0Actors[0]); - group0Actors[1]->removeFromGroup(); - group1->addActor(*group0Actors[1]); - group1Actors[0]->removeFromGroup(); - group0->addActor(*group1Actors[0]); - } - - // checks - EXPECT_EQ(families[0]->getActorCount(), 4); - EXPECT_EQ(group0->getActorCount(), 3); - EXPECT_EQ(families[1]->getActorCount(), 2); - EXPECT_EQ(group1->getActorCount(), 3); - - // damage all - { - TkActor* allActors[6]; - families[0]->getActors(allActors, 4, 0); - families[1]->getActors(allActors + 4, 2, 0); - - typedef std::pair pair; - std::set combinations; - for (auto actor : allActors) - { - combinations.emplace(pair(actor->getGroup(), &actor->getFamily())); - if (actor->getVisibleChunkCount() == 4) - { - actor->damage(getCubeSlicerProgram(), &csDamageParams1); - } - actor->damage(getCubeSlicerProgram(), &csDamageParams2); - } - EXPECT_EQ(combinations.size(), 4); - - expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each - expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each - } - - // async process 2 groups - { - EXPECT_GT(gtm1.process(2), (uint32_t)0); - EXPECT_GT(gtm0.process(2), (uint32_t)0); - uint32_t completed = 0; - while (completed < 2) - { - if (gtm1.wait(false)) - completed++; - if (gtm0.wait(false)) - completed++; - } - } - - // checks - testResults(families, expectedVisibleChunks); - EXPECT_EQ(families[0]->getActorCount(), 8); - EXPECT_EQ(ftrack1.actors.size(), 8); - EXPECT_EQ(group0->getActorCount(), 8); - EXPECT_EQ(families[1]->getActorCount(), 8); - EXPECT_EQ(ftrack2.actors.size(), 8); - EXPECT_EQ(group1->getActorCount(), 8); - - // damage till the end, aggressively - std::default_random_engine re; - { - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); - NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; - - std::vector actors; - while (1) - { - TEST_ZONE_BEGIN("damage loop"); - uint32_t n0 = families[0]->getActorCount(); - uint32_t n1 = families[1]->getActorCount(); - actors.resize(n0 + n1); - families[0]->getActors(actors.data(), n0, 0); - families[1]->getActors(actors.data() + n0, n1, 0); - - bool workTBD = false; - for (TkActor* actor : actors) - { - if (!NvBlastActorCanFracture(actor->getActorLL(), nullptr)) - { - continue; - } - - workTBD = true; - - if (actor->getGraphNodeCount() > 1) - { - actor->damage(getFalloffProgram(), &radialDamageParams); - } - else - { - actor->damage(getShearProgram(), &shearDamageParams); - } - - if (re() % 1000 < 500) - { - // switch group - TkGroup* newGroup = actor->getGroup() == group0 ? group1 : group0; - actor->removeFromGroup(); - newGroup->addActor(*actor); - } - } - TEST_ZONE_END("damage loop"); - - if (!workTBD) - break; - - // async process 2 groups - { - EXPECT_GT(gtm1.process(2), (uint32_t)0); - EXPECT_GT(gtm0.process(2), (uint32_t)0); - uint32_t completed = 0; - while (completed < 2) - { - if (gtm1.wait(false)) - completed++; - if (gtm0.wait(false)) - completed++; - } - } - } - } - - // checks - EXPECT_EQ(families[0]->getActorCount(), ftrack1.actors.size()); - EXPECT_EQ(families[1]->getActorCount(), ftrack2.actors.size()); - EXPECT_EQ(65536, families[0]->getActorCount() + families[1]->getActorCount()); - EXPECT_EQ(65536, group0->getActorCount() + group1->getActorCount()); - - gtm0.release(); - gtm1.release(); - - group0->release(); - group1->release(); - - for (auto f : families) - f->release(); - - releaseTestAssets(); - releaseFramework(); -} - -TEST_F(TkTestStrict, ActorDamageBufferedDamage) -{ - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - - // group - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - // random engine - std::default_random_engine re; - - // cube asset - GeneratorAsset cube; - TkAssetDesc assetDesc; - generateCube(cube, assetDesc, 4, 2, 3); - assetDesc.bondFlags = nullptr; - TkAsset* cubeAsset = fwk->createAsset(assetDesc); - testAssets.push_back(cubeAsset); - - // actor desc - TkActorDesc cubeAD(cubeAsset); - - // test will be repated 'trials' times. Because of random shuffle inside. - const uint32_t trials = 100; - for (uint32_t i = 0; i < trials; i++) - { - // create actor - TkActor* actor = fwk->createActor(cubeAD); - EXPECT_TRUE(actor != nullptr); - TkFamily* family = (&actor->getFamily()); - group->addActor(*actor); - - // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors - // damage 4 corners with falloff radial 4 * 2 = 8 actors - // total 16 actors - uint32_t expectedActorCount = 16; - - // fallof params - const float P = 0.5f; - const float R = 0.35f; - - // 2 of damage types would be through user's NvBlastDamageProgram, this pointer must live till group->sync() - NvBlastExtRadialDamageDesc userR0 = getRadialDamageDesc(P, P, 0, R, R); - NvBlastExtProgramParams userProgramParams0 = { &userR0, nullptr }; - - NvBlastExtRadialDamageDesc userR1 = getRadialDamageDesc(-P, P, 0, R, R); - NvBlastExtProgramParams userProgramParams1 = { &userR1, nullptr }; - - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, -P, 0, R, R); - NvBlastExtProgramParams rDamageParams0 = { &r0, nullptr }; - NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, -P, 0, R, R); - NvBlastExtProgramParams rDamageParams1 = { &r1, nullptr }; - - - // fill damage functions, shuffle and apply - { - - const uint32_t damageCount = 7; - std::vector> damageFns(damageCount); - damageFns[0] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams0); }; - damageFns[1] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams1); }; - damageFns[2] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams2); }; - damageFns[3] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams0); }; - damageFns[4] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams1); }; - damageFns[5] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams0); }; - damageFns[6] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams1); }; - - // shuffle order! - std::shuffle(std::begin(damageFns), std::end(damageFns), re); - - for (uint32_t i = 0; i < damageCount; i++) - { - damageFns[i](); - } - } - - // sync - EXPECT_GT(m_groupTM->process(), (uint32_t)0); - m_groupTM->wait(); - - const auto ac = family->getActorCount(); - - // check - EXPECT_EQ(family->getActorCount(), expectedActorCount); - EXPECT_EQ(group->getActorCount(), expectedActorCount); - - // release - std::vector actors(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - for (auto a : actors) - a->removeFromGroup(); - family->release(); - } - - group->release(); - releaseFramework(); -} - -TEST_F(TkTestStrict, CreateActor) -{ - createFramework(); - TkFramework* framework = NvBlastTkFrameworkGet(); - - const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); - - std::vector assets(assetDescCount); - - // assets - for (uint32_t i = 0; i < assetDescCount; ++i) - { - TkAssetDesc desc; - reinterpret_cast(desc) = g_assetDescs[i]; - desc.bondFlags = nullptr; - assets[i] = framework->createAsset(desc); - EXPECT_TRUE(assets[i] != nullptr); - } - - // actors - std::vector actors;; - std::vector actorFamilies;; - for (const TkAsset* asset : assets) - { - for (int i = 0; i < 2; i++) - { - TkActorDesc desc(asset); - TkActor* actor = framework->createActor(desc); - EXPECT_TRUE(actor != nullptr); - EXPECT_TRUE(actor->getActorLL() != nullptr); - //EXPECT_TRUE(&actor->getFamily() != nullptr); - EXPECT_TRUE(actor->getFamily().getActorCount() == 1); - actors.push_back(actor); - EXPECT_TRUE(std::find(actorFamilies.begin(), actorFamilies.end(), &actor->getFamily()) == actorFamilies.end()); - actorFamilies.push_back(&actor->getFamily()); - - } - } - - // framework checks - { - std::vector objects; - - // assets - { - const TkType* assetType = framework->getType(TkTypeIndex::Asset); - objects.resize(framework->getObjectCount(*assetType)); - EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *assetType) == static_cast(objects.size())); - ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)assets.data(), assets.size()); - } - - // actors -# if(0) // framework does not track actors explicitly anymore - { - const TkType* actorType = framework->getType(TkTypeIndex::Actor); - objects.resize(framework->getObjectCount(*actorType)); - EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), objects.size(), *actorType) == objects.size()); - ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actors.data(), actors.size()); - } -# endif - // families - { - const TkType* familyType = framework->getType(TkTypeIndex::Family); - objects.resize(framework->getObjectCount(*familyType)); - EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *familyType) == static_cast(objects.size())); - ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actorFamilies.data(), actorFamilies.size()); - } - } - - // release - for (TkActor* actor : actors) - { - actor->release(); - } - for (TkAsset* asset : assets) - { - asset->release(); - } - - releaseFramework(); -} - -template -TkFamily* TkBaseTest::familySerialization(TkFamily* family) -{ -#if 0 - TkFramework* fw = NvBlastTkFrameworkGet(); - - const TkType* familyType = fw->getType(TkTypeIndex::Family); - EXPECT_TRUE(familyType != nullptr); - - PsMemoryBuffer* membuf = PX_NEW(PsMemoryBuffer); - EXPECT_TRUE(membuf != nullptr); - if (membuf != nullptr) - { - const bool result = family->serialize(*membuf); - EXPECT_EQ(true, result); - if (!result) - { - return family; - } - const size_t familyActorCount = family->getActorCount(); - const TkAsset* familyAsset = family->getAsset(); - family->release(); - family = reinterpret_cast(fw->deserialize(*membuf)); - EXPECT_TRUE(family != nullptr); - if (family != nullptr) - { - EXPECT_EQ(familyActorCount, family->getActorCount()); - EXPECT_EQ(familyAsset, family->getAsset()); - } - membuf->release(); - } - - return family; -#endif - return nullptr; -} - -TEST_F(TkTestAllowWarnings, DISABLED_FamilySerialization) -{ - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - - // group - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - // random engine - std::default_random_engine re; - - // cube asset - TkAsset* cubeAsset = createCubeAsset(4, 2, 3, false); - - // actor desc - TkActorDesc cubeAD(cubeAsset); - - // create actor - TkActor* actor = fwk->createActor(cubeAD); - EXPECT_TRUE(actor != nullptr); - TkFamily* family = (&actor->getFamily()); - - // set an ID - NvBlastID id; - memcpy(id.data, "Observer-expectancy effect", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) - cubeAsset->setID(id); - - // serialize/deserialize - family = familySerialization(family); - - // fill damage functions, apply one by one and serialize family in between - { - // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors - // damage 4 corners with falloff radial 4 * 2 = 8 actors - // total 16 actors - uint32_t expectedActorCount = 16; - - // cube slicer params - CSParams csDamage0(0, 0.0f); - NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; - CSParams csDamage1(1, 0.0f); - NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; - CSParams csDamage2(2, 0.0f); - NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; - - // fallof params - const float P = 0.5f; - const float R = 0.35f; - NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, P, 0, R, R); - NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, P, 0, R, R); - NvBlastExtRadialDamageDesc r2 = getRadialDamageDesc(P, -P, 0, R, R); - NvBlastExtRadialDamageDesc r3 = getRadialDamageDesc(-P, -P, 0, R, R); - NvBlastExtProgramParams r0p = { &r0, nullptr }; - NvBlastExtProgramParams r1p = { &r1, nullptr }; - NvBlastExtProgramParams r2p = { &r2, nullptr }; - NvBlastExtProgramParams r3p = { &r3, nullptr }; - - const uint32_t damageCount = 7; - std::vector> damageFns(damageCount); - damageFns[0] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams0); }; - damageFns[1] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams1); }; - damageFns[2] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams2); }; - damageFns[3] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r0p); }; - damageFns[4] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r1p); }; - damageFns[5] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r2p); }; - damageFns[6] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r3p); }; - - std::vector actors(64); - - for (uint32_t i = 0; i < damageCount; i++) - { - actors.resize(family->getActorCount()); - family->getActors(actors.data(), static_cast(actors.size())); - - // damage - for (auto actor : actors) - { - group->addActor(*actor); - damageFns[i](actor); - } - - // sync - EXPECT_GT(m_groupTM->process(), (uint32_t)0); - m_groupTM->wait(); - - family = familySerialization(family); - } - - // check - EXPECT_EQ(family->getActorCount(), expectedActorCount); - } - - // release - family->release(); - - group->release(); - releaseFramework(); -} - -TEST_F(TkTestStrict, GroupStats) -{ - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - - // group - TkGroupDesc gdesc; - gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); - TkGroup* group = fwk->createGroup(gdesc); - EXPECT_TRUE(group != nullptr); - - m_groupTM->setGroup(group); - - TkAsset* cubeAsset = createCubeAsset(4, 2); - TkActorDesc cubeDesc(cubeAsset); - - TkActor* cubeActor1 = fwk->createActor(cubeDesc); - TkActor* cubeActor2 = fwk->createActor(cubeDesc); - TkActor* cubeActor3 = fwk->createActor(cubeDesc); - TkActor* cubeActor4 = fwk->createActor(cubeDesc); - - group->addActor(*cubeActor1); - group->addActor(*cubeActor2); - group->addActor(*cubeActor3); - group->addActor(*cubeActor4); - - NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(0.0f, 0.0f, 0.0f); - NvBlastExtProgramParams radialDamageParams = { &r0, nullptr }; - cubeActor1->damage(getFalloffProgram(), &radialDamageParams); - cubeActor2->damage(getFalloffProgram(), &radialDamageParams); - cubeActor3->damage(getFalloffProgram(), &radialDamageParams); - cubeActor4->damage(getFalloffProgram(), &radialDamageParams); - - Nv::Blast::Time time; - m_groupTM->process(); - m_groupTM->wait(); - int64_t groupTime = time.getElapsedTicks(); - - TkGroupStats gstats; - group->getStats(gstats); - - int64_t total = gstats.timers.fracture + gstats.timers.island + gstats.timers.material + gstats.timers.partition + gstats.timers.visibility; - -#if NV_PROFILE - EXPECT_GT(total, 0); // some values are reported - EXPECT_LT(groupTime, total); // total LL time is higher than group time - EXPECT_GT((double)gstats.workerTime / groupTime, 2.0); // expect some minimal speedup (including overhead) - EXPECT_EQ(4, gstats.processedActorsCount); // actors processed -#endif - - releaseFramework(); -} - -TEST_F(TkTestStrict, FractureReportSupport) -{ - createFramework(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - NvBlastChunkDesc chunkDescs[] = - { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'prnt' }, - { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'left' }, - { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'rght' }, - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor = fwk->createActor(actorDesc); - actor->userData = (void*)'root'; - - class Listener : public TkEventListener - { - void receive(const TkEvent* events, uint32_t eventCount) override - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkJointUpdateEvent::EVENT_TYPE: - FAIL() << "not expecting joints here"; - break; - - case TkFractureCommands::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - - // Group::sync still needed the family for SharedMemory management. - EXPECT_TRUE(nullptr != actor.family); - - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - } - break; - - case TkFractureEvents::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - } - break; - - case TkSplitEvent::EVENT_TYPE: - { - const TkSplitEvent* split = event.getPayload(); - - EXPECT_TRUE(nullptr != split->parentData.family); - EXPECT_EQ((void*)'root', split->parentData.userData); - EXPECT_EQ(0, split->parentData.index); - - EXPECT_EQ(2, split->numChildren); - EXPECT_EQ(1, split->children[0]->getVisibleChunkCount()); - - uint32_t visibleChunkIndex; - // child order is not mandatory - { - TkActor* a = split->children[0]; - a->getVisibleChunkIndices(&visibleChunkIndex, 1); - uint32_t li = a->getIndex(); - EXPECT_EQ(1, li); - EXPECT_EQ(split->parentData.family, &a->getFamily()); - EXPECT_EQ('left', a->getAsset()->getChunks()[visibleChunkIndex].userData); - } - - { - TkActor*a = split->children[1]; - a->getVisibleChunkIndices(&visibleChunkIndex, 1); - uint32_t ri = a->getIndex(); - EXPECT_EQ(2, ri); - EXPECT_EQ(split->parentData.family, &a->getFamily()); - EXPECT_EQ('rght', a->getAsset()->getChunks()[visibleChunkIndex].userData); - } - } - break; - - default: - FAIL() << "should not get here"; - } - } - } - } listener; - actor->getFamily().addListener(listener); - - // expected state for the original actor, see Listener - EXPECT_EQ((void*)'root', actor->userData); - EXPECT_EQ(0, actor->getIndex()); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor); - - // this will trigger hierarchical chunk fracture - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - releaseFramework(); -} - -TEST_F(TkTestStrict, FractureReportGraph) -{ - createFramework(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - NvBlastBond bondToBreak = { { 1, 0, 0 }, 1, { 0, 0, 0 }, 0 }; - NvBlastBond bondToKeep = { { 1, 0, 0 }, 1, { 10, 10, 10 }, 0 }; - NvBlastBondDesc bondDescs[] = - { - { bondToKeep, { 1, 2 } }, - { bondToBreak, { 2, 3 } }, - }; - - NvBlastChunkDesc chunkDescs[] = - { - { { 0, 0, 0 }, 2, UINT32_MAX, NvBlastChunkDesc::NoFlags, 'root' }, - { { -1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'A' }, - { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'B' }, - { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'C' }, - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 2; - assetDesc.bondDescs = bondDescs; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* rootActor = fwk->createActor(actorDesc); - rootActor->userData = (void*)'root'; - - class Listener : public TkEventListener - { - void receive(const TkEvent* events, uint32_t eventCount) override - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkJointUpdateEvent::EVENT_TYPE: - FAIL() << "not expecting joints here"; - break; - - case TkFractureCommands::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - - // Group::sync still needed the family for SharedMemory management. - EXPECT_TRUE(nullptr != actor.family); - - // original actor state is not preserved, the last test will fail - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - - // this information was invalid anyway - //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; - } - break; - - case TkFractureEvents::EVENT_TYPE: - { - const TkActorData& actor = event.getPayload()->tkActorData; - - // Group::sync still needed the family for SharedMemory management. - EXPECT_TRUE(nullptr != actor.family); - - // original actor state is not preserved, the last test will fail - EXPECT_EQ((void*)'root', actor.userData); - EXPECT_EQ(0, actor.index); - - // this information was invalid anyway - //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; - } - break; - - case TkSplitEvent::EVENT_TYPE: - { - const TkSplitEvent* split = event.getPayload(); - EXPECT_EQ((void*)'root', split->parentData.userData); - EXPECT_EQ(0, split->parentData.index); - EXPECT_EQ(2, split->numChildren); - - uint32_t visibleChunkIndex[2]; - // child order is not mandatory - { - TkActor* a = split->children[1]; - EXPECT_EQ(2, a->getVisibleChunkCount()); // chunks A and B - a->getVisibleChunkIndices(visibleChunkIndex, 2); - uint32_t actorIndex = a->getIndex(); - EXPECT_EQ(0, actorIndex); // same index as the original actor - - // visible chunk order is not mandatory - EXPECT_EQ('B', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); - EXPECT_EQ('A', a->getAsset()->getChunks()[visibleChunkIndex[1]].userData); - } - - { - TkActor* a = split->children[0]; - EXPECT_EQ(1, a->getVisibleChunkCount()); - a->getVisibleChunkIndices(visibleChunkIndex, 1); - uint32_t actorIndex = a->getIndex(); - EXPECT_EQ(2, actorIndex); - EXPECT_EQ('C', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); - } - } - break; - - default: - FAIL() << "should not get here"; - } - } - } - } listener; - rootActor->getFamily().addListener(listener); - - // expected state for the original actor, see Listener - EXPECT_EQ((void*)'root', rootActor->userData); - EXPECT_EQ(0, rootActor->getIndex()); - EXPECT_EQ(1, rootActor->getVisibleChunkCount()); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*rootActor); - - // this will trigger one bond to break - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0, 0.5f, 0.5f); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - rootActor->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - releaseFramework(); -} - -TEST_F(TkTestStrict, SplitWarning) // GWD-167 -{ - createFramework(); - - TkFramework* fwk = NvBlastTkFrameworkGet(); - - NvBlastChunkDesc chunkDescs[] = - { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' }, - { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'A' }, - { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'B' }, - { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'C' }, - { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'D' }, - { { -1,0,0 }, 1, 1, NvBlastChunkDesc::NoFlags, 'AAAA' }, - { { +1,0,0 }, 1, 2, NvBlastChunkDesc::NoFlags, 'BBBB' }, - { { -1,0,0 }, 1, 3, NvBlastChunkDesc::NoFlags, 'CCCC' }, - { { +1,0,0 }, 1, 4, NvBlastChunkDesc::NoFlags, 'DDDD' }, - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor = fwk->createActor(actorDesc); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - releaseFramework(); -} - - -TEST_F(TkTestAllowWarnings, ChangeThreadCountToZero) -{ - // tests that group still allocates memory for one worker - // by replacing to a 0 threads cpu dispatcher (warns) - // mainly relies on internal asserts - - class EventCounter : public TkEventListener - { - public: - EventCounter() :fracCommands(0), fracEvents(0) {} - - void receive(const TkEvent* events, uint32_t eventCount) - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkFractureCommands::EVENT_TYPE: - fracCommands++; - break; - case TkFractureEvents::EVENT_TYPE: - fracEvents++; - break; - default: - FAIL(); - // no split due to single chunk - // no joints - } - } - } - - uint32_t fracCommands, fracEvents; - } listener; - - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - NvBlastChunkDesc chunkDescs[] = { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor1 = fwk->createActor(actorDesc); - TkActor* actor2 = fwk->createActor(actorDesc); - TkActor* actor3 = fwk->createActor(actorDesc); - TkActor* actor4 = fwk->createActor(actorDesc); - - actor1->getFamily().addListener(listener); - actor2->getFamily().addListener(listener); - actor3->getFamily().addListener(listener); - actor4->getFamily().addListener(listener); - -#if USE_PHYSX_DISPATCHER - PxU32 affinity[] = { 1, 2, 4, 8 }; - PxDefaultCpuDispatcher* disp0 = PxDefaultCpuDispatcherCreate(0, affinity); - disp0->setRunProfiled(false); - PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); - disp4->setRunProfiled(false); -#else - TestCpuDispatcher* disp0 = new TestCpuDispatcher(0); - TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); -#endif - - m_taskman->setCpuDispatcher(*disp4); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor1); - group->addActor(*actor2); - m_taskman->setCpuDispatcher(*disp0); - //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); - group->addActor(*actor3); - group->addActor(*actor4); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor1->damage(getFalloffProgram(), &radialDamageParams); - actor2->damage(getFalloffProgram(), &radialDamageParams); - actor3->damage(getFalloffProgram(), &radialDamageParams); - actor4->damage(getFalloffProgram(), &radialDamageParams); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_EQ(4, listener.fracCommands); - EXPECT_EQ(4, listener.fracEvents); - - releaseFramework(); - - disp0->release(); - disp4->release(); -} - -TEST_F(TkTestStrict, ChangeThreadCountUp) -{ - // tests that group allocates more memory for additional workers - // by replacing to a higher thread count cpu dispatcher (warns) - // mainly relies on internal asserts - - class EventCounter : public TkEventListener - { - public: - EventCounter() :fracCommands(0), fracEvents(0) {} - - void receive(const TkEvent* events, uint32_t eventCount) - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkFractureCommands::EVENT_TYPE: - fracCommands++; - break; - case TkFractureEvents::EVENT_TYPE: - fracEvents++; - break; - default: - FAIL(); - // no split due to single chunk - // no joints - } - } - } - - uint32_t fracCommands, fracEvents; - } listener; - - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - NvBlastChunkDesc chunkDescs[] = { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor1 = fwk->createActor(actorDesc); - TkActor* actor2 = fwk->createActor(actorDesc); - TkActor* actor3 = fwk->createActor(actorDesc); - TkActor* actor4 = fwk->createActor(actorDesc); - - actor1->getFamily().addListener(listener); - actor2->getFamily().addListener(listener); - actor3->getFamily().addListener(listener); - actor4->getFamily().addListener(listener); - -#if USE_PHYSX_DISPATCHER - PxU32 affinity[] = { 1, 2, 4, 8 }; - PxDefaultCpuDispatcher* disp2 = PxDefaultCpuDispatcherCreate(2, affinity); - disp2->setRunProfiled(false); - PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); - disp4->setRunProfiled(false); -#else - TestCpuDispatcher* disp2 = new TestCpuDispatcher(2); - TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); -#endif - - m_taskman->setCpuDispatcher(*disp2); - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor1); - group->addActor(*actor2); - group->addActor(*actor3); - group->addActor(*actor4); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; - actor1->damage(getFalloffProgram(), &radialDamageParams); - actor2->damage(getFalloffProgram(), &radialDamageParams); - actor3->damage(getFalloffProgram(), &radialDamageParams); - actor4->damage(getFalloffProgram(), &radialDamageParams); - - m_taskman->setCpuDispatcher(*disp4); - //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_EQ(4, listener.fracCommands); - EXPECT_EQ(4, listener.fracEvents); - - releaseFramework(); - - disp2->release(); - disp4->release(); -} - -TEST_F(TkTestAllowWarnings, GroupNoWorkers) -{ - // tests that group still works without a taskmanager - // a warnings is expected - // mainly relies on internal asserts - - class EventCounter : public TkEventListener - { - public: - EventCounter() :fracCommands(0), fracEvents(0) {} - - void receive(const TkEvent* events, uint32_t eventCount) - { - for (uint32_t i = 0; i < eventCount; i++) - { - const TkEvent& event = events[i]; - switch (event.type) - { - case TkFractureCommands::EVENT_TYPE: - fracCommands++; - break; - case TkFractureEvents::EVENT_TYPE: - fracEvents++; - break; - default: - FAIL(); - // no split due to single chunk - // no joints - } - } - } - - uint32_t fracCommands, fracEvents; - } listener; - - createFramework(); - TkFramework* fwk = NvBlastTkFrameworkGet(); - NvBlastChunkDesc chunkDescs[] = { - { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } - }; - - TkAssetDesc assetDesc; - assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); - assetDesc.chunkDescs = chunkDescs; - assetDesc.bondCount = 0; - assetDesc.bondDescs = nullptr; - assetDesc.bondFlags = nullptr; - const TkAsset* asset = fwk->createAsset(assetDesc); - - TkActorDesc actorDesc; - actorDesc.asset = asset; - TkActor* actor1 = fwk->createActor(actorDesc); - TkActor* actor2 = fwk->createActor(actorDesc); - TkActor* actor3 = fwk->createActor(actorDesc); - TkActor* actor4 = fwk->createActor(actorDesc); - - actor1->getFamily().addListener(listener); - actor2->getFamily().addListener(listener); - actor3->getFamily().addListener(listener); - actor4->getFamily().addListener(listener); - -#if USE_PHYSX_DISPATCHER - PxDefaultCpuDispatcher* disp = PxDefaultCpuDispatcherCreate(0); -#else - TestCpuDispatcher* disp = new TestCpuDispatcher(0); -#endif - m_taskman->setCpuDispatcher(*disp); - - TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; - TkGroup* group = fwk->createGroup(groupDesc); - - m_groupTM->setGroup(group); - - group->addActor(*actor1); - group->addActor(*actor2); - group->addActor(*actor3); - group->addActor(*actor4); - - NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); - NvBlastExtProgramParams programParams = { - &radialDamage, - getDefaultMaterial() - }; - - actor1->damage(getFalloffProgram(), &programParams); - actor2->damage(getFalloffProgram(), &programParams); - actor3->damage(getFalloffProgram(), &programParams); - actor4->damage(getFalloffProgram(), &programParams); - - m_groupTM->process(); - m_groupTM->wait(); - - EXPECT_EQ(4, listener.fracCommands); - EXPECT_EQ(4, listener.fracEvents); - - disp->release(); - - releaseFramework(); -} - +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + + +#include "TkBaseTest.h" + +#include +#include +#include + +#include "PsMemoryBuffer.h" + +#include "NvBlastTime.h" + +#include "NvBlastExtPxTask.h" + +struct ExpectedVisibleChunks +{ + ExpectedVisibleChunks() :numActors(0), numChunks(0) {} + ExpectedVisibleChunks(size_t a, size_t c) :numActors(a), numChunks(c) {} + size_t numActors; size_t numChunks; +}; + +void testResults(std::vector& families, std::map& expectedVisibleChunks) +{ + size_t numActors = 0; + for (TkFamily* fam : families) + { + auto ex = expectedVisibleChunks[fam]; + EXPECT_EQ(ex.numActors, fam->getActorCount()); + numActors += ex.numActors; + std::vector actors(fam->getActorCount()); + fam->getActors(actors.data(), static_cast(actors.size())); + for (TkActor* actor : actors) + { + EXPECT_EQ(ex.numChunks, actor->getVisibleChunkCount()); + } + } + + size_t numActorsExpected = 0; + for (auto expected : expectedVisibleChunks) + { + numActorsExpected += expected.second.numActors; + } + + EXPECT_EQ(numActorsExpected, numActors); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tests +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +TEST_F(TkTestStrict, CreateFramework) +{ + createFramework(); + releaseFramework(); +} + +TEST_F(TkTestStrict, CreateAsset) +{ + createFramework(); + + createTestAssets(); + releaseTestAssets(); + + releaseFramework(); +} + +#if USE_PHYSX_DISPATCHER +TEST_F(TkTestStrict, DISABLED_MemLeak) +{ + PxFoundation* pxFoundation = PxCreateFoundation(PX_FOUNDATION_VERSION, NvBlastGetPxAllocatorCallback(), NvBlastGetPxErrorCallback()); + PxU32 affinity[] = { 1, 2, 4, 8 }; + PxDefaultCpuDispatcher* cpuDispatcher = PxDefaultCpuDispatcherCreate(4, affinity); + cpuDispatcher->setRunProfiled(false); + PxTaskManager* taskman = PxTaskManager::createTaskManager(NvBlastGetPxErrorCallback(), cpuDispatcher, nullptr); + + cpuDispatcher->release(); + taskman->release(); + pxFoundation->release(); +} +#endif + +TEST_F(TkTestStrict, ActorDamageNoGroup) +{ + createFramework(); + createTestAssets(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + TkActorDesc actorDesc; + actorDesc.asset = testAssets[0]; + TkActor* actor = fwk->createActor(actorDesc); + + const size_t bondFractureCount = 4; + NvBlastFractureBuffers commands; + NvBlastBondFractureData bdata[bondFractureCount]; + for (uint32_t i = 0; i < bondFractureCount; i++) + { + bdata[i].nodeIndex0 = 2 * i + 0; + bdata[i].nodeIndex1 = 2 * i + 1; + bdata[i].health = 1.0f; + } + commands.bondFractureCount = bondFractureCount; + commands.bondFractures = bdata; + commands.chunkFractureCount = 0; + commands.chunkFractures = nullptr; + actor->applyFracture(&commands, &commands); + + TkFamily& family = actor->getFamily(); + + EXPECT_TRUE(commands.bondFractureCount == 4); + EXPECT_TRUE(actor->isPending()); + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + group->addActor(*actor); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_FALSE(actor->isPending()); + EXPECT_EQ(2, family.getActorCount()); + + releaseFramework(); +} + +TEST_F(TkTestStrict, ActorDamageGroup) +{ + TEST_ZONE_BEGIN("ActorDamageGroup"); + + createFramework(); + createTestAssets(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + TestFamilyTracker ftrack1, ftrack2; + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + + NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); + NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; + + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + std::vector families; + TkFamily* trackedFamily; + std::map expectedVisibleChunks; + + { + TkActorDesc adesc(testAssets[0]); + + TkActor* actor1 = fwk->createActor(adesc); + EXPECT_TRUE(actor1 != nullptr); + + TkActor* actor2 = fwk->createActor(adesc); + EXPECT_TRUE(actor2 != nullptr); + + + expectedVisibleChunks[&actor1->getFamily()] = ExpectedVisibleChunks(8, 1); // full damage + expectedVisibleChunks[&actor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split + + GeneratorAsset cube; + TkAssetDesc assetDesc; + generateCube(cube, assetDesc, 5, 2); + assetDesc.bondFlags = nullptr; + + TkAsset* cubeAsset = fwk->createAsset(assetDesc); + testAssets.push_back(cubeAsset); + + TkActorDesc cubeAD(cubeAsset); + + TkActor* cubeActor1 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor1 != nullptr); + + trackedFamily = &cubeActor1->getFamily(); + cubeActor1->getFamily().addListener(ftrack1); + + TkActor* cubeActor2 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor2 != nullptr); + + expectedVisibleChunks[&cubeActor1->getFamily()] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each + expectedVisibleChunks[&cubeActor2->getFamily()] = ExpectedVisibleChunks(1, 1); // not split + + ftrack1.insertActor(cubeActor1); + ftrack2.insertActor(actor1); + + actor1->getFamily().addListener(ftrack2); + + TEST_ZONE_BEGIN("add to groups"); + group->addActor(*cubeActor1); + group->addActor(*cubeActor2); + group->addActor(*actor1); + group->addActor(*actor2); + TEST_ZONE_END("add to groups"); + + families.push_back(&cubeActor1->getFamily()); + families.push_back(&cubeActor2->getFamily()); + families.push_back(&actor1->getFamily()); + families.push_back(&actor2->getFamily()); + + cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); + actor1->damage(getFalloffProgram(), &radialDamageParams); + } + + EXPECT_FALSE(group->endProcess()); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + for (TkActor* actor : actors) + { + actor->damage(getCubeSlicerProgram(), &csDamageParams1); + } + } + expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4, 2); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + for (TkActor* actor : actors) + { + actor->damage(getCubeSlicerProgram(), &csDamageParams2); + } + } + + expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(8, 1); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + TEST_ZONE_BEGIN("damage"); + for (TkActor* actor : actors) + { + actor->damage(getFalloffProgram(), &radialDamageParams); + } + TEST_ZONE_END("damage"); + } + expectedVisibleChunks[trackedFamily] = ExpectedVisibleChunks(4096, 1); + + m_groupTM->process(); + m_groupTM->wait(); + + testResults(families, expectedVisibleChunks); + + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + TEST_ZONE_BEGIN("damage"); + for (TkActor* actor : actors) + { + actor->damage(getShearProgram(), &shearDamageParams); + } + TEST_ZONE_END("damage"); + } + + m_groupTM->process(); + m_groupTM->wait(); + + + + { + std::vector actors(trackedFamily->getActorCount()); + trackedFamily->getActors(actors.data(), static_cast(actors.size())); + TEST_ZONE_BEGIN("damage"); + for (TkActor* actor : actors) + { + actor->damage(getShearProgram(), &shearDamageParams); + } + TEST_ZONE_END("damage"); + } + + m_groupTM->process(); + m_groupTM->wait(); + + group->release(); + + TEST_ZONE_BEGIN("family release"); + trackedFamily->release(); + TEST_ZONE_END("family release"); + + releaseTestAssets(); + releaseFramework(); + + TEST_ZONE_END("ActorDamageGroup"); +} + + +TEST_F(TkTestStrict, ActorDamageMultiGroup) +{ + createFramework(); + createTestAssets(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + TestFamilyTracker ftrack1, ftrack2; + + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group0 = fwk->createGroup(gdesc); + EXPECT_TRUE(group0 != nullptr); + TkGroup* group1 = fwk->createGroup(gdesc); + EXPECT_TRUE(group1 != nullptr); + + ExtGroupTaskManager& gtm1 = *ExtGroupTaskManager::create(*m_taskman, group1); + ExtGroupTaskManager& gtm0 = *ExtGroupTaskManager::create(*m_taskman, group0); + + std::vector families(2); + std::map expectedVisibleChunks; + + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + // prepare 2 equal actors/families and damage + { + GeneratorAsset cube; + TkAssetDesc assetDesc; + generateCube(cube, assetDesc, 6, 2, 5); + assetDesc.bondFlags = nullptr; + + TkAsset* cubeAsset = fwk->createAsset(assetDesc); + testAssets.push_back(cubeAsset); + + TkActorDesc cubeAD(cubeAsset); + + TkActor* cubeActor0 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor0 != nullptr); + cubeActor0->getFamily().addListener(ftrack1); + + TkActor* cubeActor1 = fwk->createActor(cubeAD); + EXPECT_TRUE(cubeActor1 != nullptr); + cubeActor1->getFamily().addListener(ftrack2); + + ftrack1.insertActor(cubeActor0); + ftrack2.insertActor(cubeActor1); + + group0->addActor(*cubeActor0); + group1->addActor(*cubeActor1); + + families[0] = (&cubeActor0->getFamily()); + families[1] = (&cubeActor1->getFamily()); + + { + cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams0); + cubeActor0->damage(getCubeSlicerProgram(), &csDamageParams1); + + cubeActor1->damage(getCubeSlicerProgram(), &csDamageParams0); + } + + expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(4, 2); // split in 4, 2 chunks each + expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(2, 4); // split in 2, 4 chunks each + } + + // async process 2 groups + { + EXPECT_GT(gtm0.process(2), (uint32_t)0); + EXPECT_GT(gtm1.process(2), (uint32_t)0); + uint32_t completed = 0; + while (completed < 2) + { + if (gtm0.wait(false)) + completed++; + if (gtm1.wait(false)) + completed++; + } + } + + // checks + testResults(families, expectedVisibleChunks); + EXPECT_EQ(families[0]->getActorCount(), 4); + EXPECT_EQ(group0->getActorCount(), 4); + EXPECT_EQ(families[1]->getActorCount(), 2); + EXPECT_EQ(group1->getActorCount(), 2); + + // we have group0 with 4 actors 2 chunks: + // group0: [2]' [2]' [2]' [2]' (family0') + // group1: [4]'' [4]'' (family1'') + // rearrange: + // group0: [2]' [2]' [4]'' + // group1: [4]'' [2]' [2]' + { + TkActor* group0Actors[2]; + group0->getActors(group0Actors, 2, 1); // start index: 1, because..why not? + TkActor* group1Actors[2]; + group1->getActors(group1Actors, 2, 0); + group0Actors[0]->removeFromGroup(); + group1->addActor(*group0Actors[0]); + group0Actors[1]->removeFromGroup(); + group1->addActor(*group0Actors[1]); + group1Actors[0]->removeFromGroup(); + group0->addActor(*group1Actors[0]); + } + + // checks + EXPECT_EQ(families[0]->getActorCount(), 4); + EXPECT_EQ(group0->getActorCount(), 3); + EXPECT_EQ(families[1]->getActorCount(), 2); + EXPECT_EQ(group1->getActorCount(), 3); + + // damage all + { + TkActor* allActors[6]; + families[0]->getActors(allActors, 4, 0); + families[1]->getActors(allActors + 4, 2, 0); + + typedef std::pair pair; + std::set combinations; + for (auto actor : allActors) + { + combinations.emplace(pair(actor->getGroup(), &actor->getFamily())); + if (actor->getVisibleChunkCount() == 4) + { + actor->damage(getCubeSlicerProgram(), &csDamageParams1); + } + actor->damage(getCubeSlicerProgram(), &csDamageParams2); + } + EXPECT_EQ(combinations.size(), 4); + + expectedVisibleChunks[families[0]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each + expectedVisibleChunks[families[1]] = ExpectedVisibleChunks(8, 1); // split in 8, 1 chunks each + } + + // async process 2 groups + { + EXPECT_GT(gtm1.process(2), (uint32_t)0); + EXPECT_GT(gtm0.process(2), (uint32_t)0); + uint32_t completed = 0; + while (completed < 2) + { + if (gtm1.wait(false)) + completed++; + if (gtm0.wait(false)) + completed++; + } + } + + // checks + testResults(families, expectedVisibleChunks); + EXPECT_EQ(families[0]->getActorCount(), 8); + EXPECT_EQ(ftrack1.actors.size(), 8); + EXPECT_EQ(group0->getActorCount(), 8); + EXPECT_EQ(families[1]->getActorCount(), 8); + EXPECT_EQ(ftrack2.actors.size(), 8); + EXPECT_EQ(group1->getActorCount(), 8); + + // damage till the end, aggressively + std::default_random_engine re; + { + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + NvBlastExtShearDamageDesc shearDamage = getShearDamageDesc(0, 0, 0); + NvBlastExtProgramParams shearDamageParams = { &shearDamage, nullptr }; + + std::vector actors; + while (1) + { + TEST_ZONE_BEGIN("damage loop"); + uint32_t n0 = families[0]->getActorCount(); + uint32_t n1 = families[1]->getActorCount(); + actors.resize(n0 + n1); + families[0]->getActors(actors.data(), n0, 0); + families[1]->getActors(actors.data() + n0, n1, 0); + + bool workTBD = false; + for (TkActor* actor : actors) + { + if (!NvBlastActorCanFracture(actor->getActorLL(), nullptr)) + { + continue; + } + + workTBD = true; + + if (actor->getGraphNodeCount() > 1) + { + actor->damage(getFalloffProgram(), &radialDamageParams); + } + else + { + actor->damage(getShearProgram(), &shearDamageParams); + } + + if (re() % 1000 < 500) + { + // switch group + TkGroup* newGroup = actor->getGroup() == group0 ? group1 : group0; + actor->removeFromGroup(); + newGroup->addActor(*actor); + } + } + TEST_ZONE_END("damage loop"); + + if (!workTBD) + break; + + // async process 2 groups + { + EXPECT_GT(gtm1.process(2), (uint32_t)0); + EXPECT_GT(gtm0.process(2), (uint32_t)0); + uint32_t completed = 0; + while (completed < 2) + { + if (gtm1.wait(false)) + completed++; + if (gtm0.wait(false)) + completed++; + } + } + } + } + + // checks + EXPECT_EQ(families[0]->getActorCount(), ftrack1.actors.size()); + EXPECT_EQ(families[1]->getActorCount(), ftrack2.actors.size()); + EXPECT_EQ(65536, families[0]->getActorCount() + families[1]->getActorCount()); + EXPECT_EQ(65536, group0->getActorCount() + group1->getActorCount()); + + gtm0.release(); + gtm1.release(); + + group0->release(); + group1->release(); + + for (auto f : families) + f->release(); + + releaseTestAssets(); + releaseFramework(); +} + +TEST_F(TkTestStrict, ActorDamageBufferedDamage) +{ + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + + // group + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + // random engine + std::default_random_engine re; + + // cube asset + GeneratorAsset cube; + TkAssetDesc assetDesc; + generateCube(cube, assetDesc, 4, 2, 3); + assetDesc.bondFlags = nullptr; + TkAsset* cubeAsset = fwk->createAsset(assetDesc); + testAssets.push_back(cubeAsset); + + // actor desc + TkActorDesc cubeAD(cubeAsset); + + // test will be repated 'trials' times. Because of random shuffle inside. + const uint32_t trials = 100; + for (uint32_t i = 0; i < trials; i++) + { + // create actor + TkActor* actor = fwk->createActor(cubeAD); + EXPECT_TRUE(actor != nullptr); + TkFamily* family = (&actor->getFamily()); + group->addActor(*actor); + + // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors + // damage 4 corners with falloff radial 4 * 2 = 8 actors + // total 16 actors + uint32_t expectedActorCount = 16; + + // fallof params + const float P = 0.5f; + const float R = 0.35f; + + // 2 of damage types would be through user's NvBlastDamageProgram, this pointer must live till group->sync() + NvBlastExtRadialDamageDesc userR0 = getRadialDamageDesc(P, P, 0, R, R); + NvBlastExtProgramParams userProgramParams0 = { &userR0, nullptr }; + + NvBlastExtRadialDamageDesc userR1 = getRadialDamageDesc(-P, P, 0, R, R); + NvBlastExtProgramParams userProgramParams1 = { &userR1, nullptr }; + + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, -P, 0, R, R); + NvBlastExtProgramParams rDamageParams0 = { &r0, nullptr }; + NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, -P, 0, R, R); + NvBlastExtProgramParams rDamageParams1 = { &r1, nullptr }; + + + // fill damage functions, shuffle and apply + { + + const uint32_t damageCount = 7; + std::vector> damageFns(damageCount); + damageFns[0] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams0); }; + damageFns[1] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams1); }; + damageFns[2] = [&]() { actor->damage(getCubeSlicerProgram(), &csDamageParams2); }; + damageFns[3] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams0); }; + damageFns[4] = [&]() { actor->damage(getFalloffProgram(), &rDamageParams1); }; + damageFns[5] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams0); }; + damageFns[6] = [&]() { actor->damage(getFalloffProgram(), &userProgramParams1); }; + + // shuffle order! + std::shuffle(std::begin(damageFns), std::end(damageFns), re); + + for (uint32_t i = 0; i < damageCount; i++) + { + damageFns[i](); + } + } + + // sync + EXPECT_GT(m_groupTM->process(), (uint32_t)0); + m_groupTM->wait(); + + const auto ac = family->getActorCount(); + + // check + EXPECT_EQ(family->getActorCount(), expectedActorCount); + EXPECT_EQ(group->getActorCount(), expectedActorCount); + + // release + std::vector actors(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + for (auto a : actors) + a->removeFromGroup(); + family->release(); + } + + group->release(); + releaseFramework(); +} + +TEST_F(TkTestStrict, CreateActor) +{ + createFramework(); + TkFramework* framework = NvBlastTkFrameworkGet(); + + const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + + std::vector assets(assetDescCount); + + // assets + for (uint32_t i = 0; i < assetDescCount; ++i) + { + TkAssetDesc desc; + reinterpret_cast(desc) = g_assetDescs[i]; + desc.bondFlags = nullptr; + assets[i] = framework->createAsset(desc); + EXPECT_TRUE(assets[i] != nullptr); + } + + // actors + std::vector actors;; + std::vector actorFamilies;; + for (const TkAsset* asset : assets) + { + for (int i = 0; i < 2; i++) + { + TkActorDesc desc(asset); + TkActor* actor = framework->createActor(desc); + EXPECT_TRUE(actor != nullptr); + EXPECT_TRUE(actor->getActorLL() != nullptr); + //EXPECT_TRUE(&actor->getFamily() != nullptr); + EXPECT_TRUE(actor->getFamily().getActorCount() == 1); + actors.push_back(actor); + EXPECT_TRUE(std::find(actorFamilies.begin(), actorFamilies.end(), &actor->getFamily()) == actorFamilies.end()); + actorFamilies.push_back(&actor->getFamily()); + + } + } + + // framework checks + { + std::vector objects; + + // assets + { + const TkType* assetType = framework->getType(TkTypeIndex::Asset); + objects.resize(framework->getObjectCount(*assetType)); + EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *assetType) == static_cast(objects.size())); + ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)assets.data(), assets.size()); + } + + // actors +# if(0) // framework does not track actors explicitly anymore + { + const TkType* actorType = framework->getType(TkTypeIndex::Actor); + objects.resize(framework->getObjectCount(*actorType)); + EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), objects.size(), *actorType) == objects.size()); + ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actors.data(), actors.size()); + } +# endif + // families + { + const TkType* familyType = framework->getType(TkTypeIndex::Family); + objects.resize(framework->getObjectCount(*familyType)); + EXPECT_TRUE(framework->getObjects(reinterpret_cast(objects.data()), static_cast(objects.size()), *familyType) == static_cast(objects.size())); + ExpectArrayMatch(objects.data(), objects.size(), (TkObject**)actorFamilies.data(), actorFamilies.size()); + } + } + + // release + for (TkActor* actor : actors) + { + actor->release(); + } + for (TkAsset* asset : assets) + { + asset->release(); + } + + releaseFramework(); +} + +template +TkFamily* TkBaseTest::familySerialization(TkFamily* family) +{ +#if 0 + TkFramework* fw = NvBlastTkFrameworkGet(); + + const TkType* familyType = fw->getType(TkTypeIndex::Family); + EXPECT_TRUE(familyType != nullptr); + + PsMemoryBuffer* membuf = PX_NEW(PsMemoryBuffer); + EXPECT_TRUE(membuf != nullptr); + if (membuf != nullptr) + { + const bool result = family->serialize(*membuf); + EXPECT_EQ(true, result); + if (!result) + { + return family; + } + const size_t familyActorCount = family->getActorCount(); + const TkAsset* familyAsset = family->getAsset(); + family->release(); + family = reinterpret_cast(fw->deserialize(*membuf)); + EXPECT_TRUE(family != nullptr); + if (family != nullptr) + { + EXPECT_EQ(familyActorCount, family->getActorCount()); + EXPECT_EQ(familyAsset, family->getAsset()); + } + membuf->release(); + } + + return family; +#endif + return nullptr; +} + +TEST_F(TkTestAllowWarnings, DISABLED_FamilySerialization) +{ + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + + // group + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + // random engine + std::default_random_engine re; + + // cube asset + TkAsset* cubeAsset = createCubeAsset(4, 2, 3, false); + + // actor desc + TkActorDesc cubeAD(cubeAsset); + + // create actor + TkActor* actor = fwk->createActor(cubeAD); + EXPECT_TRUE(actor != nullptr); + TkFamily* family = (&actor->getFamily()); + + // set an ID + NvBlastID id; + memcpy(id.data, "Observer-expectancy effect", sizeof(NvBlastID)); // Stuffing an arbitrary 16 bytes (The prefix of the given string) + cubeAsset->setID(id); + + // serialize/deserialize + family = familySerialization(family); + + // fill damage functions, apply one by one and serialize family in between + { + // damage 3 times with CubeSlicer 2 * 2 * 2 = 8 actors + // damage 4 corners with falloff radial 4 * 2 = 8 actors + // total 16 actors + uint32_t expectedActorCount = 16; + + // cube slicer params + CSParams csDamage0(0, 0.0f); + NvBlastExtProgramParams csDamageParams0 = { &csDamage0, nullptr }; + CSParams csDamage1(1, 0.0f); + NvBlastExtProgramParams csDamageParams1 = { &csDamage1, nullptr }; + CSParams csDamage2(2, 0.0f); + NvBlastExtProgramParams csDamageParams2 = { &csDamage2, nullptr }; + + // fallof params + const float P = 0.5f; + const float R = 0.35f; + NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(P, P, 0, R, R); + NvBlastExtRadialDamageDesc r1 = getRadialDamageDesc(-P, P, 0, R, R); + NvBlastExtRadialDamageDesc r2 = getRadialDamageDesc(P, -P, 0, R, R); + NvBlastExtRadialDamageDesc r3 = getRadialDamageDesc(-P, -P, 0, R, R); + NvBlastExtProgramParams r0p = { &r0, nullptr }; + NvBlastExtProgramParams r1p = { &r1, nullptr }; + NvBlastExtProgramParams r2p = { &r2, nullptr }; + NvBlastExtProgramParams r3p = { &r3, nullptr }; + + const uint32_t damageCount = 7; + std::vector> damageFns(damageCount); + damageFns[0] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams0); }; + damageFns[1] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams1); }; + damageFns[2] = [&](TkActor* a) { a->damage(getCubeSlicerProgram(), &csDamageParams2); }; + damageFns[3] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r0p); }; + damageFns[4] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r1p); }; + damageFns[5] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r2p); }; + damageFns[6] = [&](TkActor* a) { a->damage(getFalloffProgram(), &r3p); }; + + std::vector actors(64); + + for (uint32_t i = 0; i < damageCount; i++) + { + actors.resize(family->getActorCount()); + family->getActors(actors.data(), static_cast(actors.size())); + + // damage + for (auto actor : actors) + { + group->addActor(*actor); + damageFns[i](actor); + } + + // sync + EXPECT_GT(m_groupTM->process(), (uint32_t)0); + m_groupTM->wait(); + + family = familySerialization(family); + } + + // check + EXPECT_EQ(family->getActorCount(), expectedActorCount); + } + + // release + family->release(); + + group->release(); + releaseFramework(); +} + +TEST_F(TkTestStrict, GroupStats) +{ + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + + // group + TkGroupDesc gdesc; + gdesc.workerCount = m_taskman->getCpuDispatcher()->getWorkerCount(); + TkGroup* group = fwk->createGroup(gdesc); + EXPECT_TRUE(group != nullptr); + + m_groupTM->setGroup(group); + + TkAsset* cubeAsset = createCubeAsset(4, 2); + TkActorDesc cubeDesc(cubeAsset); + + TkActor* cubeActor1 = fwk->createActor(cubeDesc); + TkActor* cubeActor2 = fwk->createActor(cubeDesc); + TkActor* cubeActor3 = fwk->createActor(cubeDesc); + TkActor* cubeActor4 = fwk->createActor(cubeDesc); + + group->addActor(*cubeActor1); + group->addActor(*cubeActor2); + group->addActor(*cubeActor3); + group->addActor(*cubeActor4); + + NvBlastExtRadialDamageDesc r0 = getRadialDamageDesc(0.0f, 0.0f, 0.0f); + NvBlastExtProgramParams radialDamageParams = { &r0, nullptr }; + cubeActor1->damage(getFalloffProgram(), &radialDamageParams); + cubeActor2->damage(getFalloffProgram(), &radialDamageParams); + cubeActor3->damage(getFalloffProgram(), &radialDamageParams); + cubeActor4->damage(getFalloffProgram(), &radialDamageParams); + + Nv::Blast::Time time; + m_groupTM->process(); + m_groupTM->wait(); + int64_t groupTime = time.getElapsedTicks(); + + TkGroupStats gstats; + group->getStats(gstats); + + int64_t total = gstats.timers.fracture + gstats.timers.island + gstats.timers.material + gstats.timers.partition + gstats.timers.visibility; + +#if NV_PROFILE + EXPECT_GT(total, 0); // some values are reported + EXPECT_LT(groupTime, total); // total LL time is higher than group time + EXPECT_GT((double)gstats.workerTime / groupTime, 2.0); // expect some minimal speedup (including overhead) + EXPECT_EQ(4, gstats.processedActorsCount); // actors processed +#endif + + releaseFramework(); +} + +TEST_F(TkTestStrict, FractureReportSupport) +{ + createFramework(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + NvBlastChunkDesc chunkDescs[] = + { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'prnt' }, + { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'left' }, + { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'rght' }, + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor = fwk->createActor(actorDesc); + actor->userData = (void*)'root'; + + class Listener : public TkEventListener + { + void receive(const TkEvent* events, uint32_t eventCount) override + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkJointUpdateEvent::EVENT_TYPE: + FAIL() << "not expecting joints here"; + break; + + case TkFractureCommands::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + + // Group::sync still needed the family for SharedMemory management. + EXPECT_TRUE(nullptr != actor.family); + + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + } + break; + + case TkFractureEvents::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + } + break; + + case TkSplitEvent::EVENT_TYPE: + { + const TkSplitEvent* split = event.getPayload(); + + EXPECT_TRUE(nullptr != split->parentData.family); + EXPECT_EQ((void*)'root', split->parentData.userData); + EXPECT_EQ(0, split->parentData.index); + + EXPECT_EQ(2, split->numChildren); + EXPECT_EQ(1, split->children[0]->getVisibleChunkCount()); + + uint32_t visibleChunkIndex; + // child order is not mandatory + { + TkActor* a = split->children[0]; + a->getVisibleChunkIndices(&visibleChunkIndex, 1); + uint32_t li = a->getIndex(); + EXPECT_EQ(1, li); + EXPECT_EQ(split->parentData.family, &a->getFamily()); + EXPECT_EQ('left', a->getAsset()->getChunks()[visibleChunkIndex].userData); + } + + { + TkActor*a = split->children[1]; + a->getVisibleChunkIndices(&visibleChunkIndex, 1); + uint32_t ri = a->getIndex(); + EXPECT_EQ(2, ri); + EXPECT_EQ(split->parentData.family, &a->getFamily()); + EXPECT_EQ('rght', a->getAsset()->getChunks()[visibleChunkIndex].userData); + } + } + break; + + default: + FAIL() << "should not get here"; + } + } + } + } listener; + actor->getFamily().addListener(listener); + + // expected state for the original actor, see Listener + EXPECT_EQ((void*)'root', actor->userData); + EXPECT_EQ(0, actor->getIndex()); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor); + + // this will trigger hierarchical chunk fracture + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + releaseFramework(); +} + +TEST_F(TkTestStrict, FractureReportGraph) +{ + createFramework(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + NvBlastBond bondToBreak = { { 1, 0, 0 }, 1, { 0, 0, 0 }, 0 }; + NvBlastBond bondToKeep = { { 1, 0, 0 }, 1, { 10, 10, 10 }, 0 }; + NvBlastBondDesc bondDescs[] = + { + { bondToKeep, { 1, 2 } }, + { bondToBreak, { 2, 3 } }, + }; + + NvBlastChunkDesc chunkDescs[] = + { + { { 0, 0, 0 }, 2, UINT32_MAX, NvBlastChunkDesc::NoFlags, 'root' }, + { { -1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'A' }, + { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'B' }, + { { +1, 0, 0 }, 1, 0, NvBlastChunkDesc::SupportFlag, 'C' }, + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 2; + assetDesc.bondDescs = bondDescs; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* rootActor = fwk->createActor(actorDesc); + rootActor->userData = (void*)'root'; + + class Listener : public TkEventListener + { + void receive(const TkEvent* events, uint32_t eventCount) override + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkJointUpdateEvent::EVENT_TYPE: + FAIL() << "not expecting joints here"; + break; + + case TkFractureCommands::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + + // Group::sync still needed the family for SharedMemory management. + EXPECT_TRUE(nullptr != actor.family); + + // original actor state is not preserved, the last test will fail + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + + // this information was invalid anyway + //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; + } + break; + + case TkFractureEvents::EVENT_TYPE: + { + const TkActorData& actor = event.getPayload()->tkActorData; + + // Group::sync still needed the family for SharedMemory management. + EXPECT_TRUE(nullptr != actor.family); + + // original actor state is not preserved, the last test will fail + EXPECT_EQ((void*)'root', actor.userData); + EXPECT_EQ(0, actor.index); + + // this information was invalid anyway + //EXPECT_EQ(1, actor->getVisibleChunkCount()) << "state not preserved"; + } + break; + + case TkSplitEvent::EVENT_TYPE: + { + const TkSplitEvent* split = event.getPayload(); + EXPECT_EQ((void*)'root', split->parentData.userData); + EXPECT_EQ(0, split->parentData.index); + EXPECT_EQ(2, split->numChildren); + + uint32_t visibleChunkIndex[2]; + // child order is not mandatory + { + TkActor* a = split->children[1]; + EXPECT_EQ(2, a->getVisibleChunkCount()); // chunks A and B + a->getVisibleChunkIndices(visibleChunkIndex, 2); + uint32_t actorIndex = a->getIndex(); + EXPECT_EQ(0, actorIndex); // same index as the original actor + + // visible chunk order is not mandatory + EXPECT_EQ('B', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); + EXPECT_EQ('A', a->getAsset()->getChunks()[visibleChunkIndex[1]].userData); + } + + { + TkActor* a = split->children[0]; + EXPECT_EQ(1, a->getVisibleChunkCount()); + a->getVisibleChunkIndices(visibleChunkIndex, 1); + uint32_t actorIndex = a->getIndex(); + EXPECT_EQ(2, actorIndex); + EXPECT_EQ('C', a->getAsset()->getChunks()[visibleChunkIndex[0]].userData); + } + } + break; + + default: + FAIL() << "should not get here"; + } + } + } + } listener; + rootActor->getFamily().addListener(listener); + + // expected state for the original actor, see Listener + EXPECT_EQ((void*)'root', rootActor->userData); + EXPECT_EQ(0, rootActor->getIndex()); + EXPECT_EQ(1, rootActor->getVisibleChunkCount()); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*rootActor); + + // this will trigger one bond to break + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0, 0.5f, 0.5f); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + rootActor->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + releaseFramework(); +} + +TEST_F(TkTestStrict, SplitWarning) // GWD-167 +{ + createFramework(); + + TkFramework* fwk = NvBlastTkFrameworkGet(); + + NvBlastChunkDesc chunkDescs[] = + { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' }, + { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'A' }, + { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'B' }, + { { -1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'C' }, + { { +1,0,0 }, 1, 0, NvBlastChunkDesc::NoFlags, 'D' }, + { { -1,0,0 }, 1, 1, NvBlastChunkDesc::NoFlags, 'AAAA' }, + { { +1,0,0 }, 1, 2, NvBlastChunkDesc::NoFlags, 'BBBB' }, + { { -1,0,0 }, 1, 3, NvBlastChunkDesc::NoFlags, 'CCCC' }, + { { +1,0,0 }, 1, 4, NvBlastChunkDesc::NoFlags, 'DDDD' }, + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor = fwk->createActor(actorDesc); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + releaseFramework(); +} + + +TEST_F(TkTestAllowWarnings, ChangeThreadCountToZero) +{ + // tests that group still allocates memory for one worker + // by replacing to a 0 threads cpu dispatcher (warns) + // mainly relies on internal asserts + + class EventCounter : public TkEventListener + { + public: + EventCounter() :fracCommands(0), fracEvents(0) {} + + void receive(const TkEvent* events, uint32_t eventCount) + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkFractureCommands::EVENT_TYPE: + fracCommands++; + break; + case TkFractureEvents::EVENT_TYPE: + fracEvents++; + break; + default: + FAIL(); + // no split due to single chunk + // no joints + } + } + } + + uint32_t fracCommands, fracEvents; + } listener; + + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + NvBlastChunkDesc chunkDescs[] = { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor1 = fwk->createActor(actorDesc); + TkActor* actor2 = fwk->createActor(actorDesc); + TkActor* actor3 = fwk->createActor(actorDesc); + TkActor* actor4 = fwk->createActor(actorDesc); + + actor1->getFamily().addListener(listener); + actor2->getFamily().addListener(listener); + actor3->getFamily().addListener(listener); + actor4->getFamily().addListener(listener); + +#if USE_PHYSX_DISPATCHER + PxU32 affinity[] = { 1, 2, 4, 8 }; + PxDefaultCpuDispatcher* disp0 = PxDefaultCpuDispatcherCreate(0, affinity); + disp0->setRunProfiled(false); + PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); + disp4->setRunProfiled(false); +#else + TestCpuDispatcher* disp0 = new TestCpuDispatcher(0); + TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); +#endif + + m_taskman->setCpuDispatcher(*disp4); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor1); + group->addActor(*actor2); + m_taskman->setCpuDispatcher(*disp0); + //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); + group->addActor(*actor3); + group->addActor(*actor4); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor1->damage(getFalloffProgram(), &radialDamageParams); + actor2->damage(getFalloffProgram(), &radialDamageParams); + actor3->damage(getFalloffProgram(), &radialDamageParams); + actor4->damage(getFalloffProgram(), &radialDamageParams); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_EQ(4, listener.fracCommands); + EXPECT_EQ(4, listener.fracEvents); + + releaseFramework(); + + disp0->release(); + disp4->release(); +} + +TEST_F(TkTestStrict, ChangeThreadCountUp) +{ + // tests that group allocates more memory for additional workers + // by replacing to a higher thread count cpu dispatcher (warns) + // mainly relies on internal asserts + + class EventCounter : public TkEventListener + { + public: + EventCounter() :fracCommands(0), fracEvents(0) {} + + void receive(const TkEvent* events, uint32_t eventCount) + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkFractureCommands::EVENT_TYPE: + fracCommands++; + break; + case TkFractureEvents::EVENT_TYPE: + fracEvents++; + break; + default: + FAIL(); + // no split due to single chunk + // no joints + } + } + } + + uint32_t fracCommands, fracEvents; + } listener; + + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + NvBlastChunkDesc chunkDescs[] = { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor1 = fwk->createActor(actorDesc); + TkActor* actor2 = fwk->createActor(actorDesc); + TkActor* actor3 = fwk->createActor(actorDesc); + TkActor* actor4 = fwk->createActor(actorDesc); + + actor1->getFamily().addListener(listener); + actor2->getFamily().addListener(listener); + actor3->getFamily().addListener(listener); + actor4->getFamily().addListener(listener); + +#if USE_PHYSX_DISPATCHER + PxU32 affinity[] = { 1, 2, 4, 8 }; + PxDefaultCpuDispatcher* disp2 = PxDefaultCpuDispatcherCreate(2, affinity); + disp2->setRunProfiled(false); + PxDefaultCpuDispatcher* disp4 = PxDefaultCpuDispatcherCreate(4, affinity); + disp4->setRunProfiled(false); +#else + TestCpuDispatcher* disp2 = new TestCpuDispatcher(2); + TestCpuDispatcher* disp4 = new TestCpuDispatcher(4); +#endif + + m_taskman->setCpuDispatcher(*disp2); + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor1); + group->addActor(*actor2); + group->addActor(*actor3); + group->addActor(*actor4); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams radialDamageParams = { &radialDamage, nullptr }; + actor1->damage(getFalloffProgram(), &radialDamageParams); + actor2->damage(getFalloffProgram(), &radialDamageParams); + actor3->damage(getFalloffProgram(), &radialDamageParams); + actor4->damage(getFalloffProgram(), &radialDamageParams); + + m_taskman->setCpuDispatcher(*disp4); + //group->setWorkerCount(m_taskman->getCpuDispatcher()->getWorkerCount()); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_EQ(4, listener.fracCommands); + EXPECT_EQ(4, listener.fracEvents); + + releaseFramework(); + + disp2->release(); + disp4->release(); +} + +TEST_F(TkTestAllowWarnings, GroupNoWorkers) +{ + // tests that group still works without a taskmanager + // a warnings is expected + // mainly relies on internal asserts + + class EventCounter : public TkEventListener + { + public: + EventCounter() :fracCommands(0), fracEvents(0) {} + + void receive(const TkEvent* events, uint32_t eventCount) + { + for (uint32_t i = 0; i < eventCount; i++) + { + const TkEvent& event = events[i]; + switch (event.type) + { + case TkFractureCommands::EVENT_TYPE: + fracCommands++; + break; + case TkFractureEvents::EVENT_TYPE: + fracEvents++; + break; + default: + FAIL(); + // no split due to single chunk + // no joints + } + } + } + + uint32_t fracCommands, fracEvents; + } listener; + + createFramework(); + TkFramework* fwk = NvBlastTkFrameworkGet(); + NvBlastChunkDesc chunkDescs[] = { + { { 0,0,0 }, 2, UINT32_MAX, NvBlastChunkDesc::SupportFlag, 'root' } + }; + + TkAssetDesc assetDesc; + assetDesc.chunkCount = sizeof(chunkDescs) / sizeof(NvBlastChunkDesc); + assetDesc.chunkDescs = chunkDescs; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.bondFlags = nullptr; + const TkAsset* asset = fwk->createAsset(assetDesc); + + TkActorDesc actorDesc; + actorDesc.asset = asset; + TkActor* actor1 = fwk->createActor(actorDesc); + TkActor* actor2 = fwk->createActor(actorDesc); + TkActor* actor3 = fwk->createActor(actorDesc); + TkActor* actor4 = fwk->createActor(actorDesc); + + actor1->getFamily().addListener(listener); + actor2->getFamily().addListener(listener); + actor3->getFamily().addListener(listener); + actor4->getFamily().addListener(listener); + +#if USE_PHYSX_DISPATCHER + PxDefaultCpuDispatcher* disp = PxDefaultCpuDispatcherCreate(0); +#else + TestCpuDispatcher* disp = new TestCpuDispatcher(0); +#endif + m_taskman->setCpuDispatcher(*disp); + + TkGroupDesc groupDesc = { m_taskman->getCpuDispatcher()->getWorkerCount() }; + TkGroup* group = fwk->createGroup(groupDesc); + + m_groupTM->setGroup(group); + + group->addActor(*actor1); + group->addActor(*actor2); + group->addActor(*actor3); + group->addActor(*actor4); + + NvBlastExtRadialDamageDesc radialDamage = getRadialDamageDesc(0, 0, 0); + NvBlastExtProgramParams programParams = { + &radialDamage, + getDefaultMaterial() + }; + + actor1->damage(getFalloffProgram(), &programParams); + actor2->damage(getFalloffProgram(), &programParams); + actor3->damage(getFalloffProgram(), &programParams); + actor4->damage(getFalloffProgram(), &programParams); + + m_groupTM->process(); + m_groupTM->wait(); + + EXPECT_EQ(4, listener.fracCommands); + EXPECT_EQ(4, listener.fracEvents); + + disp->release(); + + releaseFramework(); +} + -- cgit v1.2.3