hkDemo::Result BroadphaseAddRemoveDemo::stepDemo() { #if defined(SELF_BENCHMARK) LARGE_INTEGER counter0; printf("Benchmarking...\r\n"); ::QueryPerformanceCounter(&counter0); for(int i=0;i<512;++i) { spawnFirework(); } LARGE_INTEGER counter1; LARGE_INTEGER frequency; ::QueryPerformanceCounter(&counter1); ::QueryPerformanceFrequency(&frequency); printf("Time: %.I64d ms\r\n",((counter1.QuadPart-counter0.QuadPart)*1000)/frequency.QuadPart); exit(1); #endif if(m_env->m_window->getKeyboard().wasKeyPressed('B')) { m_useBatch=!m_useBatch; } if(m_env->m_window->getKeyboard().wasKeyPressed('D')) { m_draw=!m_draw; } if(m_env->m_gamePad->wasButtonPressed(HKG_PAD_BUTTON_2)) m_useBatch=!m_useBatch; if(m_env->m_gamePad->wasButtonPressed(HKG_PAD_BUTTON_3)) m_draw=!m_draw; m_world->lock(); m_timer-=m_world->getSolverInfo()->m_deltaTime; if(m_timer<0) { HK_TIMER_BEGIN_LIST( "DemoAddBodies", "Add" ); spawnFirework(); if(m_bodies.getSize()>=m_maxbodies) m_timer+=m_rate; HK_TIMER_SPLIT_LIST( "Remove" ); flushFirework(); HK_TIMER_END_LIST(); } m_world->unlock(); char text[256]; hkString::sprintf(text,"%d bodies [%d bodies per second]\r\n[B]atchs: %s\r\n[D]raw: %s",m_bodies.getSize(),(int)(m_count/m_rate), m_useBatch?"ON":"OFF", m_draw?"ON":"OFF"); m_env->m_textDisplay->outputText(text,8,(int)m_env->m_window->getHeight()-96); return hkDefaultPhysicsDemo::stepDemo(); }
hkDemo::Result OptimizedWorldRaycastDemo::stepDemo() { m_world->lock(); m_time += m_timestep; // // Create a number of ray directions // const int NUM_DIRECTIONS = 8; const hkReal increment = 2.0f; //const int NUM_DIRECTIONS = 125; const hkReal increment = 0.5f; hkVector4 rayDirections[NUM_DIRECTIONS]; { int i = 0; for (hkReal x = -1.0f; x <= 1.0f; x += increment) { for (hkReal y = -1.0f; y <= 1.0f; y += increment) { for (hkReal z = -1.0f; z <= 1.0f; z+= increment) { rayDirections[i++].set(x,y,z); } } } } hkReal angle = 0.5f * m_time; // In these first chapter we simply call the hkpWorld castRay version with no further optimizations { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; HK_TIMER_BEGIN("NotOptimized", HK_NULL); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); m_world->castRay(inputs[i], collectors[i]); } } HK_TIMER_END(); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::RED); } // This second set of raycasts (yellow) is using a the group functionality of the hkpWorldRayCaster. // Internally it builds an the aabb cache. This is an optimization for broadphase raycasts. // Note that we need to pass in array of collectors to the hkpWorldRayCaster. // As the hkpWorldRayCaster has no clue about the size of each collector, we have to pass in the size. // Interesting if we use a size of 0, than all raycast will report to the same collector. { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; // // Cast Rays // HK_TIMER_BEGIN("CastRayGroup", HK_NULL); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); } hkpWorldRayCaster rayCaster; rayCaster.castRayGroup( *m_world->getBroadPhase(), inputs, NUM_DIRECTIONS, m_world->getCollisionFilter(), collectors, sizeof(collectors[0]) ); } HK_TIMER_END(); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::CYAN); } // This set of raycasts (yellow) is using a the hkpBroadPhaseAabbCache. // So by building the aabb cache. This is an optimization for broadphase raycasts. // The idea is that the aabb cache is actually a reduced broadphase, just storing // objects inside the aabb. Therefore using this cache can speed up rayCasts significantly. // Unfortunately we cannot use our simple hkpWorld::castRay() function, but have to use // a small helper class hkpWorldRayCaster (which is actually used by the hkpWorld::castRay()). { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; // // Calc Cache // HK_TIMER_BEGIN_LIST("UseAabbCache", "CalcCache"); hkpBroadPhaseAabbCache* cache; int cacheSize; { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); cacheSize = m_world->getBroadPhase()->getAabbCacheSize(); cache = reinterpret_cast<hkpBroadPhaseAabbCache*>(hkAllocateStack<char>(cacheSize)); m_world->getBroadPhase()->calcAabbCache( aabb, cache ); } // // Cast Rays // HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); hkpWorldRayCaster rayCaster; rayCaster.castRay( *m_world->getBroadPhase(), inputs[i], m_world->getCollisionFilter(), cache, collectors[i] ); } } HK_TIMER_END_LIST(); hkDeallocateStack<char>( (char*)cache); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::YELLOW); } // This third set of raycasts (blue) is using a the hkpBroadPhaseAabbCache and // also makes use of the fact that many rays starting at the same position can // be handled specially. // Unfortunately we cannot use our simple hkpWorld::castRay() function, but have to use // a small helper class hkpWorldRayCaster (which is actually used by the hkpWorld::castRay()). { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; // // Calc Cache // HK_TIMER_BEGIN_LIST("CacheSameStart", "CalcCache"); hkpBroadPhaseAabbCache* cache; int cacheSize; { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); cacheSize = m_world->getBroadPhase()->getAabbCacheSize(); cache = reinterpret_cast<hkpBroadPhaseAabbCache*>(hkAllocateStack<char>(cacheSize)); m_world->getBroadPhase()->calcAabbCache( aabb, cache ); } // // Cast Rays // HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); } hkpWorldRayCaster rayCaster; rayCaster.castRaysFromSinglePoint( *m_world->getBroadPhase(), inputs, NUM_DIRECTIONS, m_world->getCollisionFilter(), cache, collectors, hkSizeOf( collectors[0] ) ); } HK_TIMER_END_LIST(); hkDeallocateStack<char>( (char*)cache); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::BLUE); } // This 4th set of raycasts (green) is using a the AabbPhantom // A phantom is like a persistent aabb cache, so it only makes sense if our aabb shows some // framecoherency. // Unfortunetaly the current phantom implementations simply casts the ray against all objects // overlapping with the phantom. Therefor, if too many objects are intersecting the phantoms // aabb, performance could get bad. { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; HK_TIMER_BEGIN_LIST("AabbPhantom", "MovePhantom"); { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); m_phantom->setAabb( aabb ); } HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); m_phantom->castRay(inputs[i], collectors[i]); } } HK_TIMER_END_LIST(); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::GREEN); } // fastest (purple), combine all optimizations above // We are using a phantom to set up the broadphase cache. This avoids the problem of the hkpPhantom::castRay() { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; hkpBroadPhaseAabbCache* cache; int cacheSize; HK_TIMER_BEGIN_LIST("AllOpt.", "MovePhantomAndDoCache"); { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); m_phantomUseCache->setAabb( aabb ); cacheSize = m_world->getBroadPhase()->getAabbCacheSize(); cache = reinterpret_cast<hkpBroadPhaseAabbCache*>(hkAllocateStack<char>(cacheSize)); m_world->getBroadPhase()->calcAabbCache( m_phantomUseCache->getOverlappingCollidables(), cache ); } HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); } hkpWorldRayCaster rayCaster; rayCaster.castRaysFromSinglePoint( *m_world->getBroadPhase(), inputs, NUM_DIRECTIONS, m_world->getCollisionFilter(), cache, collectors, hkSizeOf( collectors[0] ) ); } HK_TIMER_END_LIST(); hkDeallocateStack<char>( (char*)cache); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::PURPLE); } m_world->unlock(); return hkDefaultPhysicsDemo::stepDemo(); }
void AabbQueryDemo::queryAabbMT() { HK_TIMER_BEGIN_LIST("queryAabbMT", "setup"); hkAabb aabb; // Grab a body from the world, and compute it's AABB + some padding const hkpCollidable* queryCollidable; { queryCollidable = m_collidables[m_collIdx]; hkpRigidBody* rb = hkGetRigidBody(queryCollidable); queryCollidable->getShape()->getAabb(rb->getTransform(), 20.0f, aabb); } hkArray<hkpKdTreeAabbCommand> commands; commands.setSize(numQueries); // // For performance timings, we query the same AABB multiple times and overwrite the results. // When using this, make sure to use different output arrays! // hkArray<hkPrimitiveId> output; output.setSize(50); // // Set up the commands for the job // for (int i=0; i<commands.getSize(); i++) { hkpKdTreeAabbCommand& command = commands[i]; command.m_aabb = aabb; command.m_results = output.begin(); command.m_resultsCapacity = output.getSize(); command.m_numResultsOut = 0; } // // Setup the job // hkArray<hkpRayCastQueryJobHeader> header(1); hkpKdTreeAabbJob aabbJob(header.begin(), commands.begin(), commands.getSize(), &m_semaphore); aabbJob.m_numTrees = 1; aabbJob.m_trees[0] = m_world->m_kdTreeManager->getTree(); m_jobQueue->addJob( *reinterpret_cast<hkJobQueue::JobQueueEntry*>(&aabbJob), hkJobQueue::JOB_HIGH_PRIORITY ); HK_TIMER_SPLIT_LIST("process"); m_jobThreadPool->processAllJobs( m_jobQueue ); m_jobThreadPool->waitForCompletion(); m_semaphore.acquire(); HK_TIMER_END_LIST(); // // Run the same query on the broadphase for comparison purposes // hkArray<hkpBroadPhaseHandlePair> sapHits; { HK_TIME_CODE_BLOCK("BroadphaseQueryAabb", HK_NULL); for (int i=0; i<numQueries; i++) { sapHits.clear(); m_world->getBroadPhase()->querySingleAabb( aabb, sapHits ); } } // // Check results and draw // for (int i=0; i<commands.getSize(); i++) { hkpKdTreeAabbCommand& command = commands[i]; hkBool jobOk = compareHitArrays(command.m_aabb, command.m_results, command.m_numResultsOut, sapHits); for (int j=0; j<command.m_numResultsOut; j++) { HK_SET_OBJECT_COLOR(command.m_results[j], hkColor::YELLOW); } HK_SET_OBJECT_COLOR((hkUlong)queryCollidable, hkColor::LIME); if( !jobOk ) { m_env->m_textDisplay->outputText("MT Hit lits differed!", 20, 250, (hkUint32) hkColor::RED); } } }