diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp b/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp deleted file mode 100644 index e72bd9b12c..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp +++ /dev/null @@ -1,1277 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -// Modifications copyright Amazon.com, Inc. or its affiliates. - -///BvDynamicTree implementation by Nathanael Presson -#include - -namespace GridMate -{ - // - struct btDbvtNodeEnumerator : BvDynamicTree::ICollideCollector - { - BvDynamicTree::ConstNodeArrayType nodes; - void Process(const BvDynamicTree::NodeType* n) { nodes.push_back(n); } - }; - - // - static AZ_FORCE_INLINE int indexof(const BvDynamicTree::NodeType* node) - { - return (node->m_parent->m_childs[1]==node); - } - - // - static AZ_FORCE_INLINE BvDynamicTree::VolumeType merge( const BvDynamicTree::VolumeType& a, const BvDynamicTree::VolumeType& b) - { - BvDynamicTree::VolumeType res; - Merge(a,b,res); - return res; - } - - // volume+edge lengths - static AZ_FORCE_INLINE float size(const BvDynamicTree::VolumeType& a) - { - const AZ::Vector3 edges = a.GetExtents(); - return edges.GetX()*edges.GetY()*edges.GetZ() + edges.Dot(AZ::Vector3::CreateOne()); - } - - // - static void getmaxdepth(const BvDynamicTree::NodeType* node,int depth,int& maxdepth) - { - if(node->IsInternal()) - { - getmaxdepth(node->m_childs[0],depth+1,maxdepth); - getmaxdepth(node->m_childs[0],depth+1,maxdepth); - } - else - maxdepth= AZ::GetMax(maxdepth,depth); - } - - - - //========================================================================= - // insertleaf - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::insertleaf( NodeType* root, NodeType* leaf) - { - if(!m_root) - { - m_root = leaf; - leaf->m_parent = 0; - } - else - { - if(!root->IsLeaf()) - { - do { - root=root->m_childs[Select( leaf->m_volume, - root->m_childs[0]->m_volume, - root->m_childs[1]->m_volume)]; - } while(!root->IsLeaf()); - } - NodeType* prev = root->m_parent; - NodeType* node = createnode(prev,leaf->m_volume,root->m_volume,0); - if(prev) - { - prev->m_childs[indexof(root)] = node; - node->m_childs[0] = root;root->m_parent=node; - node->m_childs[1] = leaf;leaf->m_parent=node; - do { - if(!prev->m_volume.Contains(node->m_volume)) - Merge(prev->m_childs[0]->m_volume,prev->m_childs[1]->m_volume,prev->m_volume); - else - break; - node=prev; - } while(0!=(prev=node->m_parent)); - } - else - { - node->m_childs[0] = root;root->m_parent=node; - node->m_childs[1] = leaf;leaf->m_parent=node; - m_root = node; - } - } - } - - //========================================================================= - // removeleaf - // [3/4/2009] - //========================================================================= - BvDynamicTree::NodeType* - BvDynamicTree::removeleaf( NodeType* leaf) - { - if(leaf==m_root) - { - m_root=0; - return 0; - } - else - { - NodeType* parent=leaf->m_parent; - NodeType* prev=parent->m_parent; - NodeType* sibling=parent->m_childs[1-indexof(leaf)]; - if(prev) - { - prev->m_childs[indexof(parent)]=sibling; - sibling->m_parent=prev; - deletenode(parent); - while(prev) - { - const VolumeType pb=prev->m_volume; - Merge(prev->m_childs[0]->m_volume,prev->m_childs[1]->m_volume,prev->m_volume); - if(NotEqual(pb,prev->m_volume)) - { - prev=prev->m_parent; - } else break; - } - return prev?prev:m_root; - } - else - { - m_root=sibling; - sibling->m_parent=0; - deletenode(parent); - return m_root; - } - } - } - - - //========================================================================= - // fetchleaves - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::fetchleaves(NodeType* root,NodeArrayType& leaves,int depth) - { - if(root->IsInternal()&&depth) - { - fetchleaves(root->m_childs[0],leaves,depth-1); - fetchleaves(root->m_childs[1],leaves,depth-1); - deletenode(root); - } - else - { - leaves.push_back(root); - } - } - - //========================================================================= - // split - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::split(const NodeArrayType& leaves, NodeArrayType& left, NodeArrayType& right, const AZ::Vector3& org, const AZ::Vector3& axis) - { - left.resize(0); - right.resize(0); - for(size_t i = 0, ni = leaves.size(); i < ni; ++i) - { - if (axis.Dot(leaves[i]->m_volume.GetCenter() - org) < 0.0f) - { - left.push_back(leaves[i]); - } - else - { - right.push_back(leaves[i]); - } - } - } - - //========================================================================= - // bounds - // [3/4/2009] - //========================================================================= - BvDynamicTree::VolumeType - BvDynamicTree::bounds(const NodeArrayType& leaves) - { - VolumeType volume=leaves[0]->m_volume; - for(size_t i=1,ni=leaves.size();im_volume,volume); - } - return volume; - } - - //========================================================================= - // bottomup - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::bottomup( NodeArrayType& leaves ) - { - while(leaves.size()>1) - { - float minsize = std::numeric_limits::max(); - int minidx[2]={-1,-1}; - for(unsigned int i=0;im_volume,leaves[j]->m_volume)); - if(szm_volume,n[1]->m_volume,0); - p->m_childs[0] = n[0]; - p->m_childs[1] = n[1]; - n[0]->m_parent = p; - n[1]->m_parent = p; - leaves[minidx[0]] = p; - //leaves.swap(minidx[1],leaves.size()-1); - leaves[minidx[1]] = leaves.back(); - leaves.pop_back(); - } - } - - //========================================================================= - // topdown - // [3/4/2009] - //========================================================================= - BvDynamicTree::NodeType* - BvDynamicTree::topdown(NodeArrayType& leaves,int bu_treshold) - { - static const AZ::Vector3 axis[]= { AZ::Vector3(1.0f,0.0f,0.0f), AZ::Vector3(0.0f,1.0f,0.0f), AZ::Vector3(0.0f,0.0f,1.0f)}; - if(leaves.size()>1) - { - if(leaves.size()>(unsigned int)bu_treshold) - { - const VolumeType vol=bounds(leaves); - const AZ::Vector3 org=vol.GetCenter(); - NodeArrayType sets[2]; - int bestaxis=-1; - int bestmidp=(int)leaves.size(); - int splitcount[3][2]={{0,0},{0,0},{0,0}}; - - for(unsigned int i=0;im_volume.GetCenter()-org; - for(int j=0;j<3;++j) - { - ++splitcount[j][x.Dot(axis[j]) > 0.0f ? 1 : 0]; - } - } - for(unsigned int i=0;i<3;++i) - { - if((splitcount[i][0]>0)&&(splitcount[i][1]>0)) - { - // todo just remove the sign bit... - const int midp = (int)fabsf((float)(splitcount[i][0]-splitcount[i][1])); - if(midp=0) - { - sets[0].reserve(splitcount[bestaxis][0]); - sets[1].reserve(splitcount[bestaxis][1]); - split(leaves,sets[0],sets[1],org,axis[bestaxis]); - } - else - { - sets[0].reserve(leaves.size()/2+1); - sets[1].reserve(leaves.size()/2); - for(size_t i=0,ni=leaves.size();im_childs[0] = topdown(sets[0],bu_treshold); - node->m_childs[1] = topdown(sets[1],bu_treshold); - node->m_childs[0]->m_parent=node; - node->m_childs[1]->m_parent=node; - return(node); - } - else - { - bottomup(leaves); - return(leaves[0]); - } - } - return(leaves[0]); - } - - //========================================================================= - // sort - // [3/4/2009] - //========================================================================= - AZ_FORCE_INLINE BvDynamicTree::NodeType* - BvDynamicTree::sort(NodeType* n,NodeType*& r) - { - BvDynamicTree::NodeType* p=n->m_parent; - AZ_Assert(n->IsInternal(), "We can call this only for internal nodes!"); - if(p>n) - { - const int i=indexof(n); - const int j=1-i; - NodeType* s=p->m_childs[j]; - NodeType* q=p->m_parent; - AZ_Assert(n==p->m_childs[i], ""); - if(q) q->m_childs[indexof(p)]=n; else r=n; - s->m_parent=n; - p->m_parent=n; - n->m_parent=q; - p->m_childs[0]=n->m_childs[0]; - p->m_childs[1]=n->m_childs[1]; - n->m_childs[0]->m_parent=p; - n->m_childs[1]->m_parent=p; - n->m_childs[i]=p; - n->m_childs[j]=s; - AZStd::swap(p->m_volume,n->m_volume); - return(p); - } - return(n); - } - - #if 0 - static DBVT_INLINE NodeType* walkup(NodeType* n,int count) - { - while(n&&(count--)) n=n->parent; - return(n); - } - #endif - - // - // Api - // - - // - BvDynamicTree::BvDynamicTree() - { - m_root = 0; - m_free = 0; - m_lkhd = -1; - m_leaves = 0; - m_opath = 0; - } - - // - BvDynamicTree::~BvDynamicTree() - { - Clear(); - } - - // - void BvDynamicTree::Clear() - { - if(m_root) recursedeletenode(m_root); - delete m_free; - m_free=0; - } - - // - void BvDynamicTree::OptimizeBottomUp() - { - if(m_root) - { - NodeArrayType leaves; - leaves.reserve(m_leaves); - fetchleaves(m_root,leaves); - bottomup(leaves); - m_root=leaves[0]; - } - } - - // - void - BvDynamicTree::OptimizeTopDown(int bu_treshold) - { - if(m_root) - { - NodeArrayType leaves; - leaves.reserve(m_leaves); - fetchleaves(m_root,leaves); - m_root=topdown(leaves,bu_treshold); - } - } - - // - void - BvDynamicTree::OptimizeIncremental(int passes) - { - if(passes<0) passes=m_leaves; - if(m_root&&(passes>0)) - { - do { - NodeType* node=m_root; - unsigned bit=0; - while(node->IsInternal()) - { - node=sort(node,m_root)->m_childs[(m_opath>>bit)&1]; - bit=(bit+1)&(sizeof(unsigned)*8-1); - } - Update(node); - ++m_opath; - } while(--passes); - } - } - - // - BvDynamicTree::NodeType* - BvDynamicTree::Insert(const VolumeType& volume,void* data) - { - NodeType* leaf=createnode(0,volume,data); - insertleaf(m_root,leaf); - ++m_leaves; - return(leaf); - } - - // - void - BvDynamicTree::Update(NodeType* leaf,int lookahead) - { - NodeType* root=removeleaf(leaf); - if(root) - { - if(lookahead>=0) - { - for(int i=0;(im_parent;++i) - { - root=root->m_parent; - } - } else root=m_root; - } - insertleaf(root,leaf); - } - - // - void - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume) - { - NodeType* root=removeleaf(leaf); - if(root) - { - if(m_lkhd>=0) - { - for(int i=0;(im_parent;++i) - { - root=root->m_parent; - } - } else root=m_root; - } - leaf->m_volume=volume; - insertleaf(root,leaf); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const AZ::Vector3& velocity,const float margin) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.Expand(AZ::Vector3(margin)); - volume.SignedExpand(velocity); - Update(leaf,volume); - return(true); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const AZ::Vector3& velocity) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.SignedExpand(velocity); - Update(leaf,volume); - return(true); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const float margin) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.Expand(AZ::Vector3(margin)); - Update(leaf,volume); - return(true); - } - - // - void - BvDynamicTree::Remove(NodeType* leaf) - { - removeleaf(leaf); - deletenode(leaf); - --m_leaves; - } - - template - int findLinearSearch(BvDynamicTree::ConstNodeArrayType& arr, const T& key) - { - size_t numElements = arr.size(); - int index = (int)numElements; - - for(size_t i=0;iPrepare(m_root,(unsigned int)nodes.nodes.size()); - for(unsigned int i=0;i<(unsigned int)nodes.nodes.size();++i) - { - const NodeType* n=nodes.nodes[i]; - int p=-1; - if(n->m_parent) p = findLinearSearch(nodes.nodes,n->m_parent); - if(n->IsInternal()) - { - const int c0=findLinearSearch(nodes.nodes,n->m_childs[0]); - const int c1=findLinearSearch(nodes.nodes,n->m_childs[1]); - iwriter->WriteNode(n,i,p,c0,c1); - } - else - { - iwriter->WriteLeaf(n,i,p); - } - } - } - - // - void - BvDynamicTree::Clone(BvDynamicTree& dest,IClone* iclone) const - { - dest.Clear(); - if(m_root!=0) - { - vector stack; - stack.reserve(m_leaves); - stack.push_back(sStkCLN(m_root,0)); - do { - const size_t i=stack.size()-1; - const sStkCLN e=stack[i]; - NodeType* n= dest.createnode(e.parent,e.node->m_volume,e.node->m_data); - stack.pop_back(); - if(e.parent!=0) - e.parent->m_childs[i&1]=n; - else - dest.m_root=n; - if(e.node->IsInternal()) - { - stack.push_back(sStkCLN(e.node->m_childs[0],n)); - stack.push_back(sStkCLN(e.node->m_childs[1],n)); - } - else - { - iclone->CloneLeaf(n); - } - } while(!stack.empty()); - } - } - - // - int - BvDynamicTree::GetMaxDepth(const NodeType* node) - { - int depth=0; - if(node) getmaxdepth(node,1,depth); - return depth ; - } - - // - int - BvDynamicTree::CountLeaves(const NodeType* node) - { - if(node->IsInternal()) - return(CountLeaves(node->m_childs[0])+CountLeaves(node->m_childs[1])); - else - return(1); - } - - // - void - BvDynamicTree::ExtractLeaves(const NodeType* node,vector& leaves) - { - if(node->IsInternal()) - { - ExtractLeaves(node->m_childs[0],leaves); - ExtractLeaves(node->m_childs[1],leaves); - } - else - { - leaves.push_back(node); - } - } - - // - #if DBVT_ENABLE_BENCHMARK - - #include - #include - #include - - /* - q6600,2.4ghz - - /Ox /Ob2 /Oi /Ot /I "." /I "..\.." /I "..\..\src" /D "NDEBUG" /D "_LIB" /D "_WINDOWS" /D "_CRT_SECURE_NO_DEPRECATE" /D "_CRT_NONSTDC_NO_DEPRECATE" /D "WIN32" - /GF /FD /MT /GS- /Gy /arch:SSE2 /Zc:wchar_t- /Fp"..\..\out\release8\build\libbulletcollision\libbulletcollision.pch" - /Fo"..\..\out\release8\build\libbulletcollision\\" - /Fd"..\..\out\release8\build\libbulletcollision\bulletcollision.pdb" - /W3 /nologo /c /Wp64 /Zi /errorReport:prompt - - Benchmarking dbvt... - World scale: 100.000000 - Extents base: 1.000000 - Extents range: 4.000000 - Leaves: 8192 - sizeof(VolumeType): 32 bytes - sizeof(NodeType): 44 bytes - [1] VolumeType intersections: 3499 ms (-1%) - [2] VolumeType merges: 1934 ms (0%) - [3] BvDynamicTree::collideTT: 5485 ms (-21%) - [4] BvDynamicTree::collideTT self: 2814 ms (-20%) - [5] BvDynamicTree::collideTT xform: 7379 ms (-1%) - [6] BvDynamicTree::collideTT xform,self: 7270 ms (-2%) - [7] BvDynamicTree::rayTest: 6314 ms (0%),(332143 r/s) - [8] insert/remove: 2093 ms (0%),(1001983 ir/s) - [9] updates (teleport): 1879 ms (-3%),(1116100 u/s) - [10] updates (jitter): 1244 ms (-4%),(1685813 u/s) - [11] optimize (incremental): 2514 ms (0%),(1668000 o/s) - [12] VolumeType notequal: 3659 ms (0%) - [13] culling(OCL+fullsort): 2218 ms (0%),(461 t/s) - [14] culling(OCL+qsort): 3688 ms (5%),(2221 t/s) - [15] culling(KDOP+qsort): 1139 ms (-1%),(7192 t/s) - [16] insert/remove batch(256): 5092 ms (0%),(823704 bir/s) - [17] VolumeType select: 3419 ms (0%) - */ - - struct btDbvtBenchmark - { - struct NilPolicy : BvDynamicTree::ICollide - { - NilPolicy() : m_pcount(0),m_depth(-SIMD_INFINITY),m_checksort(true) {} - void Process(const NodeType*,const NodeType*) { ++m_pcount; } - void Process(const NodeType*) { ++m_pcount; } - void Process(const NodeType*,btScalar depth) - { - ++m_pcount; - if(m_checksort) - { if(depth>=m_depth) m_depth=depth; else printf("wrong depth: %f (should be >= %f)\r\n",depth,m_depth); } - } - int m_pcount; - btScalar m_depth; - bool m_checksort; - }; - struct P14 : BvDynamicTree::ICollide - { - struct Node - { - const NodeType* leaf; - btScalar depth; - }; - void Process(const NodeType* leaf,btScalar depth) - { - Node n; - n.leaf = leaf; - n.depth = depth; - } - static int sortfnc(const Node& a,const Node& b) - { - if(a.depthb.depth) return(-1); - return(0); - } - btAlignedObjectArray m_nodes; - }; - struct P15 : BvDynamicTree::ICollide - { - struct Node - { - const NodeType* leaf; - btScalar depth; - }; - void Process(const NodeType* leaf) - { - Node n; - n.leaf = leaf; - n.depth = dot(leaf->volume.GetCenter(),m_axis); - } - static int sortfnc(const Node& a,const Node& b) - { - if(a.depthb.depth) return(-1); - return(0); - } - btAlignedObjectArray m_nodes; - btAZ::Vector3 m_axis; - }; - static btScalar RandUnit() - { - return(rand()/(btScalar)RAND_MAX); - } - static btAZ::Vector3 RandAZ::Vector3() - { - return(btAZ::Vector3(RandUnit(),RandUnit(),RandUnit())); - } - static btAZ::Vector3 RandAZ::Vector3(btScalar cs) - { - return(RandAZ::Vector3()*cs-btAZ::Vector3(cs,cs,cs)/2); - } - static VolumeType RandVolume(btScalar cs,btScalar eb,btScalar es) - { - return(VolumeType::FromCE(RandAZ::Vector3(cs),btAZ::Vector3(eb,eb,eb)+RandAZ::Vector3()*es)); - } - static btTransform RandTransform(btScalar cs) - { - btTransform t; - t.setOrigin(RandAZ::Vector3(cs)); - t.setRotation(btQuaternion(RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2).normalized()); - return(t); - } - static void RandTree(btScalar cs,btScalar eb,btScalar es,int leaves,BvDynamicTree& dbvt) - { - dbvt.clear(); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i transforms; - btDbvtBenchmark::NilPolicy policy; - transforms.resize(cfgBenchmark5_Iterations); - for(int i=0;i transforms; - btDbvtBenchmark::NilPolicy policy; - transforms.resize(cfgBenchmark6_Iterations); - for(int i=0;i rayorg; - btAlignedObjectArray raydir; - btDbvtBenchmark::NilPolicy policy; - rayorg.resize(cfgBenchmark7_Iterations); - raydir.resize(cfgBenchmark7_Iterations); - for(int i=0;i leaves; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - dbvt.extractLeaves(dbvt.m_root,leaves); - printf("[9] updates (teleport): "); - wallclock.reset(); - for(int i=0;i(leaves[rand()%cfgLeaves]), - btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale)); - } - } - const int time=(int)wallclock.getTimeMilliseconds(); - const int up=cfgBenchmark9_Passes*cfgBenchmark9_Iterations; - printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark9_Reference)*100/time,up*1000/time); - } - if(cfgBenchmark10_Enable) - {// Benchmark 10 - srand(380843); - BvDynamicTree dbvt; - btAlignedObjectArray leaves; - btAlignedObjectArray vectors; - vectors.resize(cfgBenchmark10_Iterations); - for(int i=0;i(leaves[rand()%cfgLeaves]); - VolumeType v=VolumeType::FromMM(l->volume.GetMin()+d,l->volume.GetMax()+d); - dbvt.update(l,v); - } - } - const int time=(int)wallclock.getTimeMilliseconds(); - const int up=cfgBenchmark10_Passes*cfgBenchmark10_Iterations; - printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark10_Reference)*100/time,up*1000/time); - } - if(cfgBenchmark11_Enable) - {// Benchmark 11 - srand(380843); - BvDynamicTree dbvt; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - printf("[11] optimize (incremental): "); - wallclock.reset(); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i vectors; - btDbvtBenchmark::NilPolicy policy; - vectors.resize(cfgBenchmark13_Iterations); - for(int i=0;i vectors; - btDbvtBenchmark::P14 policy; - vectors.resize(cfgBenchmark14_Iterations); - for(int i=0;i vectors; - btDbvtBenchmark::P15 policy; - vectors.resize(cfgBenchmark15_Iterations); - for(int i=0;i batch; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - batch.reserve(cfgBenchmark16_BatchCount); - printf("[16] Insert/remove batch(%u): ",cfgBenchmark16_BatchCount); - wallclock.reset(); - for(int i=0;i volumes; - btAlignedObjectArray results; - btAlignedObjectArray indices; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - indices.resize(cfgLeaves); - for(int i=0;i -#include -#include - -#include -#include - -namespace GridMate -{ - namespace Internal - { - /** - * - */ - class DynamicTreeAabb : public AZ::Aabb - { - public: - GM_CLASS_ALLOCATOR(DynamicTreeAabb); - - AZ_FORCE_INLINE explicit DynamicTreeAabb() {} - AZ_FORCE_INLINE DynamicTreeAabb(const AZ::Aabb& aabb) : AZ::Aabb(aabb) {} - AZ_FORCE_INLINE explicit DynamicTreeAabb(const AZ::Vector3& min,const AZ::Vector3& max) : AZ::Aabb(AZ::Aabb::CreateFromMinMax(min,max)) {} - - AZ_FORCE_INLINE static DynamicTreeAabb CreateFromFacePoints(const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c) - { - DynamicTreeAabb vol(a,a); - vol.AddPoint(b); - vol.AddPoint(c); - return vol; - } - - AZ_FORCE_INLINE void SignedExpand(const AZ::Vector3& e) - { - AZ::Vector3 zero = AZ::Vector3::CreateZero(); - AZ::Vector3 mxE = m_max + e; - AZ::Vector3 miE = m_min + e; - m_max = AZ::Vector3::CreateSelectCmpGreater(e,zero,mxE,m_max ); - m_min = AZ::Vector3::CreateSelectCmpGreater(e,zero,m_min,miE); - } - AZ_FORCE_INLINE int Classify(const AZ::Vector3& n,const float o,int s) const - { - AZ::Vector3 pi, px; - switch(s) - { - case (0+0+0): px=m_min; - pi=m_max; break; - case (1+0+0): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());break; - case (0+2+0): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());break; - case (1+2+0): px=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());break; - case (0+0+4): px=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());break; - case (1+0+4): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());break; - case (0+2+4): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());break; - case (1+2+4): px=m_max; - pi=m_min;break; - } - - if (n.Dot(px) + o < 0.0f) - { - return -1; - } - if (n.Dot(pi) + o > 0.0f) - { - return 1; - } - - return 0; - } - AZ_FORCE_INLINE float ProjectMinimum(const AZ::Vector3& v, unsigned signs) const - { - const AZ::Vector3* b[]={&m_max,&m_min}; - const AZ::Vector3 p( b[(signs>>0)&1]->GetX(),b[(signs>>1)&1]->GetY(),b[(signs>>2)&1]->GetZ()); - return p.Dot(v); - } - - // Move the code here - AZ_FORCE_INLINE friend bool IntersectAabbAabb(const DynamicTreeAabb& a,const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend bool IntersectAabbPoint(const DynamicTreeAabb& a, const AZ::Vector3& b); - AZ_FORCE_INLINE friend bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b); - AZ_FORCE_INLINE friend float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend int Select(const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r); - AZ_FORCE_INLINE friend bool NotEqual(const DynamicTreeAabb& a, const DynamicTreeAabb& b); - private: - AZ_FORCE_INLINE void AddSpan(const AZ::Vector3& d, float& smi, float& smx) const - { - AZ::Vector3 vecZero = AZ::Vector3::CreateZero(); - AZ::Vector3 mxD = m_max*d; - AZ::Vector3 miD = m_min*d; - AZ::Vector3 smiAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,mxD,miD); - AZ::Vector3 smxAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,miD,mxD); - AZ::Vector3 vecOne = AZ::Vector3::CreateOne(); - // sum components - smi += smiAdd.Dot(vecOne); - smx += smxAdd.Dot(vecOne); - } - }; - - // - AZ_FORCE_INLINE bool IntersectAabbAabb(const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return a.Overlaps(b); - } - - AZ_FORCE_INLINE bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b) - { - //use plane normal to quickly select the nearest corner of the aabb - AZ::Vector3 testPoint = AZ::Vector3::CreateSelectCmpGreater(b.GetNormal(), AZ::Vector3::CreateZero(), a.GetMin(), a.GetMax()); - //test if nearest point is inside the plane - return b.GetPointDist(testPoint) <= 0.0f; - } - - // - AZ_FORCE_INLINE float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - const AZ::Vector3 d=(a.m_min+a.m_max)-(b.m_min+b.m_max); - // get abs and sum - return d.GetAbs().Dot(AZ::Vector3::CreateOne()); - } - - // - AZ_FORCE_INLINE int Select( const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return Proximity(o,a) < Proximity(o,b); - } - - // - AZ_FORCE_INLINE void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r) - { - r.m_min = AZ::Vector3::CreateSelectCmpGreater(b.m_min,a.m_min,a.m_min,b.m_min); - r.m_max = AZ::Vector3::CreateSelectCmpGreater(a.m_max,b.m_max,a.m_max,b.m_max); - } - - // - AZ_FORCE_INLINE bool NotEqual( const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return (a.m_min != b.m_min || a.m_max != b.m_max); - } - - - /* NodeType */ - struct DynamicTreeNode - { - GM_CLASS_ALLOCATOR(DynamicTreeNode); - - DynamicTreeAabb m_volume; - DynamicTreeNode* m_parent; - AZ_FORCE_INLINE bool IsLeaf() const { return(m_childs[1]==0); } - AZ_FORCE_INLINE bool IsInternal() const { return(!IsLeaf()); } - union - { - DynamicTreeNode* m_childs[2]; - void* m_data; - int m_dataAsInt; - }; - }; - } - - /** - * Implementation of dynamic aabb tree, based on the bullet dynamic tree (btDbvt). - * - * The BvDynamicTree class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree). - * This BvDynamicTree is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes. - * Unlike the BvTreeQuantized, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure. - */ - class BvDynamicTree - { - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(BvDynamicTree); - - typedef Internal::DynamicTreeAabb VolumeType; - typedef Internal::DynamicTreeNode NodeType; - - typedef vector NodeArrayType; - typedef vector ConstNodeArrayType; - - private: - - /* Stack element */ - struct sStkNN - { - const NodeType* a; - const NodeType* b; - sStkNN() {} - sStkNN(const NodeType* na,const NodeType* nb) : a(na), b(nb) {} - }; - struct sStkNP - { - const NodeType* node; - int mask; - sStkNP(const NodeType* n, unsigned m) : node(n), mask(m) {} - }; - struct sStkNPS - { - const NodeType* node; - int mask; - float value; - sStkNPS() {} - sStkNPS(const NodeType* n, unsigned m, const float v) : node(n), mask(m), value(v) {} - }; - struct sStkCLN - { - const NodeType* node; - NodeType* parent; - sStkCLN(const NodeType* n, NodeType* p) : node(n), parent(p) {} - }; - - public: - /* ICollideCollector templated collectors should implement this functions or inherit from this class */ - struct ICollideCollector - { - void Process(const NodeType*, const NodeType*) {} - void Process(const NodeType*) {} - void Process(const NodeType* n, const float) { Process(n); } - bool Descent(const NodeType*) { return true; } - bool AllLeaves(const NodeType*) { return true; } - }; - - /* IWriter */ - struct IWriter - { - virtual ~IWriter() {} - virtual void Prepare(const NodeType* root,int numnodes) = 0; - virtual void WriteNode(const NodeType*, int index, int parent, int child0, int child1) = 0; - virtual void WriteLeaf(const NodeType*, int index, int parent) = 0; - }; - /* IClone */ - struct IClone - { - virtual ~IClone() {} - virtual void CloneLeaf(NodeType*) {} - }; - - // Constants - enum - { - SIMPLE_STACKSIZE = 64, - DOUBLE_STACKSIZE = SIMPLE_STACKSIZE * 2 - }; - - // Methods - BvDynamicTree(); - ~BvDynamicTree(); - - NodeType* GetRoot() const { return m_root; } - void Clear(); - bool Empty() const { return 0 == m_root; } - int GetNumLeaves() const { return m_leaves; } - void OptimizeBottomUp(); - void OptimizeTopDown(int bu_treshold = 128); - void OptimizeIncremental(int passes); - NodeType* Insert(const VolumeType& box,void* data); - void Update(NodeType* leaf, int lookahead=-1); - void Update(NodeType* leaf, VolumeType& volume); - bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity, const float margin); - bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity); - bool Update(NodeType* leaf, VolumeType& volume, const float margin); - void Remove(NodeType* leaf); - void Write(IWriter* iwriter) const; - void Clone(BvDynamicTree& dest, IClone* iclone=0) const; - static int GetMaxDepth(const NodeType* node); - static int CountLeaves(const NodeType* node); - static void ExtractLeaves(const NodeType* node, /*btAlignedObjectArray&*/vector& leaves); - #if DBVT_ENABLE_BENCHMARK - static void Benchmark(); - #else - static void Benchmark(){} - #endif - /** - * Collector should inherit from ICollide - */ - template - static inline void enumNodes( const NodeType* root, Collector& collector) - { - collector.Process(root); - if(root->IsInternal()) - { - enumNodes(root->m_childs[0],collector); - enumNodes(root->m_childs[1],collector); - } - } - template - static void enumLeaves( const NodeType* root,Collector& collector) - { - if(root->IsInternal()) - { - enumLeaves(root->m_childs[0],collector); - enumLeaves(root->m_childs[1],collector); - } - else - { - collector.Process(root); - } - } - template - void collideTT( const NodeType* root0,const NodeType* root1,Collector& collector) const - { - if(root0&&root1) - { - size_t depth=1; - size_t treshold=DOUBLE_STACKSIZE-4; - vector stkStack; - stkStack.resize(DOUBLE_STACKSIZE); - stkStack[0]=sStkNN(root0,root1); - do { - sStkNN p=stkStack[--depth]; - if(depth>treshold) - { - stkStack.resize(stkStack.size()*2); - treshold=stkStack.size()-4; - } - if(p.a==p.b) - { - if(p.a->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]); - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]); - } - } - else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume)) - { - if(p.a->IsInternal()) - { - if(p.b->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]); - } - else - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b); - } - } - else - { - if(p.b->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]); - } - else - { - collector.Process(p.a,p.b); - } - } - } - } while(depth); - } - } - template - void collideTTpersistentStack( const NodeType* root0, const NodeType* root1,Collector& collector) - { - if(root0&&root1) - { - size_t depth=1; - size_t treshold=DOUBLE_STACKSIZE-4; - - m_stkStack.resize(DOUBLE_STACKSIZE); - m_stkStack[0]=sStkNN(root0,root1); - do - { - sStkNN p=m_stkStack[--depth]; - if(depth>treshold) - { - m_stkStack.resize(m_stkStack.size()*2); - treshold=m_stkStack.size()-4; - } - if(p.a==p.b) - { - if(p.a->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]); - } - } - else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume)) - { - if(p.a->IsInternal()) - { - if(p.b->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]); - } - else - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b); - } - } - else - { - if(p.b->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]); - } - else - { - collector.Process(p.a,p.b); - } - } - } - } while(depth); - } - } - template - void collideTV( const NodeType* root, const VolumeType& volume, Collector& collector) const - { - if(root) - { -// ATTRIBUTE_ALIGNED16(VolumeType) volume(vol); -// btAlignedObjectArray stack; - AZStd::fixed_vector stack; - //stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(root); - do { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if(IntersectAabbAabb(n->m_volume,volume)) - { - if(n->IsInternal()) - { - stack.push_back(n->m_childs[0]); - stack.push_back(n->m_childs[1]); - } - else - { - collector.Process(n); - } - } - } while(!stack.empty()); - } - } - - template - void collideTP(const NodeType* root, const AZ::Plane& plane, Collector& collector) const - { - if (root) - { - AZStd::fixed_vector stack; - stack.push_back(root); - do - { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if (IntersectAabbPlane(n->m_volume, plane)) - { - if(n->IsInternal()) - { - stack.push_back(n->m_childs[0]); - stack.push_back(n->m_childs[1]); - } - else - { - collector.Process(n); - } - } - } while (!stack.empty()); - } - } - - ///rayTest is a re-entrant ray test, and can be called in parallel as long as the btAlignedAlloc is thread-safe (uses locking etc) - ///rayTest is slower than rayTestInternal, because it builds a local stack, using memory allocations, and it recomputes signs/rayDirectionInverses each time - template - static void rayTest( const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, Collector& collector) - { - if(root) - { - AZ::Vector3 ray = rayTo-rayFrom; - AZ::Vector3 rayDir = ray.GetNormalized(); - - ///what about division by zero? --> just set rayDirection[i] to INF/1e30 - AZ::Vector3 rayDirectionInverse = AZ::Vector3::CreateSelectCmpEqual(rayDir,AZ::Vector3::CreateZero(),AZ::Vector3(1e30),rayDir.GetReciprocal()); - - unsigned int signs[3];// = { rayDirectionInverse[0] < 0.0f, rayDirectionInverse[1] < 0.0f, rayDirectionInverse[2] < 0.0f }; - signs[0] = rayDirectionInverse.GetX() < 0.0f; - signs[1] = rayDirectionInverse.GetY() < 0.0f; - signs[2] = rayDirectionInverse.GetZ() < 0.0f; - - //float lambda_max = rayDir.Dot(ray); - - AZ::Vector3 resultNormal; - - //btAlignedObjectArray stack; - vector stack; - - int depth=1; - int treshold=DOUBLE_STACKSIZE-2; - - stack.resize(DOUBLE_STACKSIZE); - stack[0]=root; - AZ::Vector3 bounds[2]; - do { - const NodeType* node=stack[--depth]; - - bounds[0] = node->m_volume.GetMin(); - bounds[1] = node->m_volume.GetMax(); - - //float tmin = 1.0f; - //float lambda_min = 0.0f; - // todo.. - unsigned int result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/0; -#ifdef COMPARE_BTRAY_AABB2 - float param = 1.0f; - bool result2 = /*btRayAabb(rayFrom,rayTo,node->volume.GetMin(),node->volume.GetMax(),param,resultNormal)*/0; - AZ_Assert(result1 == result2, ""); -#endif //TEST_BTRAY_AABB2 - if(result1) - { - if(node->IsInternal()) - { - if(depth>treshold) - { - stack.resize(stack.size()*2); - treshold=stack.size()-2; - } - stack[depth++]=node->m_childs[0]; - stack[depth++]=node->m_childs[1]; - } - else - { - collector.Process(node); - } - } - } while(depth); - - } - } - - ///rayTestInternal is faster than rayTest, because it uses a persistent stack (to reduce dynamic memory allocations to a minimum) and it uses precomputed signs/rayInverseDirections - ///rayTestInternal is used by btDbvtBroadphase to accelerate world ray casts - template - void rayTestInternal(const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, const AZ::Vector3& rayDirectionInverse, unsigned int signs[3], const float lambda_max, const AZ::Vector3& aabbMin, const AZ::Vector3& aabbMax, Collector& collector) const - { - (void)rayFrom;(void)rayTo;(void)rayDirectionInverse;(void)signs;(void)lambda_max; - if(root) - { - AZ::Vector3 resultNormal; - - int depth=1; - int treshold=DOUBLE_STACKSIZE-2; - vector stack; - stack.resize(DOUBLE_STACKSIZE); - stack[0]=root; - AZ::Vector3 bounds[2]; - do - { - const NodeType* node=stack[--depth]; - bounds[0] = node->m_volume.GetMin()+aabbMin; - bounds[1] = node->m_volume.GetMax()+aabbMax; - - //float tmin = 1.0f; - //float lambda_min = 0.0f; - unsigned int result1=false; - // todo... - result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/false; - if(result1) - { - if(node->IsInternal()) - { - if(depth>treshold) - { - stack.resize(stack.size()*2); - treshold=stack.size()-2; - } - stack[depth++]=node->m_childs[0]; - stack[depth++]=node->m_childs[1]; - } - else - { - collector.Process(node); - } - } - } while(depth); - } - } - - template - static void collideKDOP(const NodeType* root, const AZ::Vector3* normals, const float* offsets, int count, Collector& collector) - { - (void)root;(void)normals;(void)offsets;(void)count;(void)collector; -/* if(root) - { - const int inside=(1< stack; - int signs[sizeof(unsigned)*8]; - btAssert(count=0)?1:0)+ - ((normals[i].y()>=0)?2:0)+ - ((normals[i].z()>=0)?4:0); - } - stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(sStkNP(root,0)); - do { - sStkNP se=stack[stack.size()-1]; - bool out=false; - stack.pop_back(); - for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); - switch(side) - { - case -1: out=true;break; - case +1: se.mask|=j;break; - } - } - } - if(!out) - { - if((se.mask!=inside)&&(se.node->isinternal())) - { - stack.push_back(sStkNP(se.node->childs[0],se.mask)); - stack.push_back(sStkNP(se.node->childs[1],se.mask)); - } - else - { - if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy); - } - } - } while(!stack.empty()); - }*/ - } - template - static void collideOCL( const NodeType* root, const AZ::Vector3* normals, const float* offsets, const AZ::Vector3& sortaxis, int count, Collector& collector, bool fullsort=true) - { - (void)root;(void)normals;(void)offsets;(void)sortaxis;(void)count;(void)offsets;(void)collector;(void)fullsort; -/* if(root) - { - const unsigned srtsgns=(sortaxis[0]>=0?1:0)+ - (sortaxis[1]>=0?2:0)+ - (sortaxis[2]>=0?4:0); - const int inside=(1< stock; - btAlignedObjectArray ifree; - btAlignedObjectArray stack; - int signs[sizeof(unsigned)*8]; - btAssert(count=0)?1:0)+ - ((normals[i].y()>=0)?2:0)+ - ((normals[i].z()>=0)?4:0); - } - stock.reserve(SIMPLE_STACKSIZE); - stack.reserve(SIMPLE_STACKSIZE); - ifree.reserve(SIMPLE_STACKSIZE); - stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns)))); - do { - const int id=stack[stack.size()-1]; - sStkNPS se=stock[id]; - stack.pop_back();ifree.push_back(id); - if(se.mask!=inside) - { - bool out=false; - for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); - switch(side) - { - case -1: out=true;break; - case +1: se.mask|=j;break; - } - } - } - if(out) continue; - } - if(policy.Descent(se.node)) - { - if(se.node->isinternal()) - { - const NodeType* pns[]={ se.node->childs[0],se.node->childs[1]}; - sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)), - sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))}; - const int q=nes[0].value0)) - { - // Insert 0 - j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size()); - stack.push_back(0); -#if DBVT_USE_MEMMOVE - memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); -#else - for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; -#endif - stack[j]=allocate(ifree,stock,nes[q]); - // Insert 1 - j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size()); - stack.push_back(0); -#if DBVT_USE_MEMMOVE - memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); -#else - for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; -#endif - stack[j]=allocate(ifree,stock,nes[1-q]); - } - else - { - stack.push_back(allocate(ifree,stock,nes[q])); - stack.push_back(allocate(ifree,stock,nes[1-q])); - } - } - else - { - policy.Process(se.node,se.value); - } - } - } while(stack.size()); - }*/ - } - - template - static void collideTU(const NodeType* root, Collector& collector) - { - (void)root;(void)collector; -/* if(root) - { - btAlignedObjectArray stack; - stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(root); - do { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if(policy.Descent(n)) - { - if(n->isinternal()) - { stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); } - else - { policy.Process(n); } - } - } while(stack.size()>0); - }*/ - } - - private: - BvDynamicTree(const BvDynamicTree&) {} - - // Helpers - //static AZ_FORCE_INLINE int nearest(const int* i,const BvDynamicTree::sStkNPS* a,const float& v,int l,int h) - //{ - // int m=0; - // while(l>1; - // if(a[i[m]].value>=v) l=m+1; else h=m; - // } - // return h; - //} - //static AZ_FORCE_INLINE int allocate( int_fixed_stack_type& ifree, stknps_fixed_stack_type& stock, const sStkNPS& value) - //{ - // int i; - // if( !ifree.empty() ) - // { - // i=ifree[ifree.size()-1]; - // ifree.pop_back(); - // stock[i]=value; - // } - // else - // { - // i=stock.size(); - // stock.push_back(value); - // } - // return i; - //} - // - - AZ_FORCE_INLINE void deletenode( NodeType* node) - { - //btAlignedFree(pdbvt->m_free); - delete m_free; - m_free=node; - } - - void recursedeletenode( NodeType* node) - { - if(!node->IsLeaf()) - { - recursedeletenode(node->m_childs[0]); - recursedeletenode(node->m_childs[1]); - } - - if( node == m_root ) m_root=0; - deletenode(node); - } - - - AZ_FORCE_INLINE NodeType* createnode( NodeType* parent, void* data) - { - NodeType* node; - if(m_free) - { node=m_free;m_free=0; } - else - { node = aznew NodeType(); } - node->m_parent = parent; - node->m_data = data; - node->m_childs[1] = 0; - return node; - } - AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume, void* data) - { - NodeType* node = createnode(parent,data); - node->m_volume=volume; - return node; - } - // - AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume0, const VolumeType& volume1, void* data) - { - NodeType* node = createnode(parent,data); - Merge(volume0,volume1,node->m_volume); - return node; - } - void insertleaf( NodeType* root, NodeType* leaf); - NodeType* removeleaf( NodeType* leaf); - void fetchleaves(NodeType* root,NodeArrayType& leaves,int depth=-1); - void split(const NodeArrayType& leaves,NodeArrayType& left,NodeArrayType& right,const AZ::Vector3& org,const AZ::Vector3& axis); - VolumeType bounds(const NodeArrayType& leaves); - void bottomup( NodeArrayType& leaves ); - NodeType* topdown(NodeArrayType& leaves,int bu_treshold); - AZ_FORCE_INLINE NodeType* sort(NodeType* n,NodeType*& r); - - NodeType* m_root; - NodeType* m_free; - int m_lkhd; - int m_leaves; - unsigned m_opath; - - //btAlignedObjectArray m_stkStack; - // Profile and choose static or dynamic vector. - typedef AZStd::fixed_vector stknn_fixed_stack_type; - typedef AZStd::fixed_vector int_fixed_stack_type; - typedef AZStd::fixed_vector stknps_fixed_stack_type; - - stknn_fixed_stack_type m_stkStack; - }; -} - -#endif // RR_DYNAMIC_BOUNDING_VOLUME_TREE_H -#pragma once diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp b/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp deleted file mode 100644 index 13a0151bc7..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include - -#include -#include -#include - -#include -#include - -// for highly verbose internal debugging -//#define INTERNAL_DEBUG_PROXIMITY - -namespace GridMate -{ - - void ProximityInterestChunk::OnReplicaActivate(const ReplicaContext& rc) - { - m_interestHandler = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4))); - AZ_Warning("GridMate", m_interestHandler, "No proximity interest handler in the user context"); - - if (m_interestHandler) - { - m_interestHandler->OnNewRulesChunk(this, rc.m_peer); - } - } - - void ProximityInterestChunk::OnReplicaDeactivate(const ReplicaContext& rc) - { - if (rc.m_peer && m_interestHandler) - { - m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer); - } - } - - bool ProximityInterestChunk::AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx) - { - if (IsProxy()) - { - auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer); - rulePtr->Set(bbox); - m_rules.insert(AZStd::make_pair(netId, rulePtr)); - } - - return true; - } - - bool ProximityInterestChunk::RemoveRuleFn(RuleNetworkId netId, const RpcContext&) - { - if (IsProxy()) - { - m_rules.erase(netId); - } - - return true; - } - - bool ProximityInterestChunk::UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&) - { - if (IsProxy()) - { - auto it = m_rules.find(netId); - if (it != m_rules.end()) - { - it->second->Set(bbox); - } - } - - return true; - } - - bool ProximityInterestChunk::AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&) - { - ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId); - if (peerChunk) - { - auto it = peerChunk->m_rules.find(netId); - if (it == peerChunk->m_rules.end()) - { - auto rulePtr = m_interestHandler->CreateRule(peerId); - peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr)); - rulePtr->Set(bbox); - } - } - return false; - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterest - */ - ProximityInterest::ProximityInterest(ProximityInterestHandler* handler) - : m_handler(handler) - , m_bbox(AZ::Aabb::CreateNull()) - { - AZ_Assert(m_handler, "Invalid interest handler"); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestRule - */ - void ProximityInterestRule::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateRule(this); - } - - void ProximityInterestRule::Destroy() - { - m_handler->DestroyRule(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestAttribute - */ - void ProximityInterestAttribute::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateAttribute(this); - } - - void ProximityInterestAttribute::Destroy() - { - m_handler->DestroyAttribute(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestHandler - */ - ProximityInterestHandler::ProximityInterestHandler() - : m_im(nullptr) - , m_rm(nullptr) - , m_lastRuleNetId(0) - , m_rulesReplica(nullptr) - { - m_attributeWorld = AZStd::make_unique(); - AZ_Assert(m_attributeWorld, "Out of memory"); - } - - ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId) - { - ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId()); - if (m_rm && peerId == m_rm->GetLocalPeerId()) - { - m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get()); - } - - CreateAndInsertIntoSpatialStructure(rulePtr); - - return rulePtr; - } - - ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId) - { - auto newAttribute = aznew ProximityInterestAttribute(this, replicaId); - AZ_Assert(newAttribute, "Out of memory"); - - CreateAndInsertIntoSpatialStructure(newAttribute); - - return newAttribute; - } - - void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule) - { - //TODO: should be pool-allocated - delete rule; - } - - void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId()); - } - - MarkAttributesDirtyInRule(rule); - - rule->m_bbox = AZ::Aabb::CreateNull(); - m_removedRules.insert(rule); - m_localRules.erase(rule); - } - - void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get()); - } - - m_dirtyRules.insert(rule); - } - - void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib) - { - delete attrib; - } - - void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib) - { - RemoveFromSpatialStructure(attrib); - - m_attributes.erase(attrib); - m_removedAttributes.insert(attrib); - } - - void ProximityInterestHandler::RemoveFromSpatialStructure(ProximityInterestAttribute* attribute) - { - attribute->m_bbox = AZ::Aabb::CreateNull(); - m_attributeWorld->Remove(attribute->GetNode()); - attribute->SetNode(nullptr); - } - - void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib) - { - auto node = attrib->GetNode(); - AZ_Assert(node, "Attribute wasn't created correctly"); - node->m_volume = attrib->Get(); - m_attributeWorld->Update(node); - - m_dirtyAttributes.insert(attrib); - } - - void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - if (chunk != m_rulesReplica) // non-local - { - m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk)); - - for (auto& rule : m_localRules) - { - chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get()); - } - } - } - - void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - (void)chunk; - m_peerChunks.erase(peer->GetId()); - } - - RuleNetworkId ProximityInterestHandler::GetNewRuleNetId() - { - ++m_lastRuleNetId; - - if (m_rulesReplica) - { - return m_rulesReplica->GetReplicaId() | (static_cast(m_lastRuleNetId) << 32); - } - - return (static_cast(m_lastRuleNetId) << 32); - } - - ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId) - { - auto it = m_peerChunks.find(peerId); - if (it == m_peerChunks.end()) - { - return nullptr; - } - - return it->second; - } - - const InterestMatchResult& ProximityInterestHandler::GetLastResult() - { - return m_resultCache; - } - - ProximityInterestHandler::RuleSet& ProximityInterestHandler::GetAffectedRules() - { - /* - * The expectation that lots of attributes will change frequently, - * so there is no point in trying to optimize cases - * where only a few attributes have changed. - */ - if (m_dirtyAttributes.empty() && !m_dirtyRules.empty()) - { - return m_dirtyRules; - } - - /* - * Assuming all rules might have been affected. - * - * There is an optimization chance here if the number of rules is large, as in 1,000+ rules. - * To handle such scale we would need another spatial structure for rules. - */ - return m_localRules; - } - - void ProximityInterestHandler::GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes) - { - m_attributeWorld->Query(rule->Get(), nodes); - } - - void ProximityInterestHandler::ClearDirtyState() - { - m_dirtyAttributes.clear(); - m_dirtyRules.clear(); - } - - void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute) - { - m_attributes.insert(attribute); - SpatialIndex::Node* node = m_attributeWorld->Insert(attribute->Get(), attribute); - attribute->SetNode(node); - } - - void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule) - { - m_localRules.insert(rule); - } - - void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result) - { - /* - * The goal is to return all dirty attributes that were either dirty because: - * 1) they changed which rules have apply to - * 2) rules have changed and no longer apply to those attributes - * and thus resulted in different peer(s) associated with a given replica. - */ - - const RuleSet& rules = GetAffectedRules(); - - for (auto& dirtyAttribute : m_dirtyAttributes) - { - result.insert(dirtyAttribute->GetReplicaId()); - } - - /* - * The exectation is to have a lot more attributes than rules. - * The amount of rules should grow linear with amount of peers, - * so it should be OK to iterate through all rules each update. - */ - for (auto& rule : rules) - { - CheckChangesForRule(rule, result); - } - - for (auto& removedRule : m_removedRules) - { - FreeRule(removedRule); - } - m_removedRules.clear(); - - // mark removed attribute as having no peers - for (auto& removedAttribute : m_removedAttributes) - { - result.insert(removedAttribute->GetReplicaId()); - FreeAttribute(removedAttribute); - } - m_removedAttributes.clear(); - } - - void ProximityInterestHandler::CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result) - { - SpatialIndex::NodeCollector collector; - GetAttributesWithinRule(rule, collector); - - auto peerId = rule->GetPeerId(); - for (ProximityInterestAttribute* attr : collector.GetNodes()) - { - AZ_Assert(attr, "bad node?"); - - auto findIt = result.find(attr->GetReplicaId()); - if (findIt != result.end()) - { - findIt->second.insert(peerId); - } - else - { - auto resultIt = result.insert(attr->GetReplicaId()); - AZ_Assert(resultIt.second, "Successfully inserted"); - resultIt.first->second.insert(peerId); - } - } - } - - void ProximityInterestHandler::MarkAttributesDirtyInRule(ProximityInterestRule* rule) - { - SpatialIndex::NodeCollector collector; - GetAttributesWithinRule(rule, collector); - - for (ProximityInterestAttribute* attr : collector.GetNodes()) - { - AZ_Assert(attr, "bad node?"); - - UpdateAttribute(attr); - } - } - - void ProximityInterestHandler::ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after) - { - m_resultCache.clear(); - -#if defined(INTERNAL_DEBUG_PROXIMITY) - before.PrintMatchResult("before"); - after.PrintMatchResult("after"); -#endif - - /* - * 'after' contains only the stuff that might have changed - */ - for (auto& possiblyDirty : after) - { - ReplicaId repId = possiblyDirty.first; - const InterestPeerSet& peerSet = possiblyDirty.second; - - auto foundInBefore = before.find(repId); - if (foundInBefore != before.end()) - { - if (!HasSamePeers(foundInBefore->second, peerSet)) - { - // was in the last calculation but has a different peer set now - m_resultCache.insert(AZStd::make_pair(repId, peerSet)); - } - } - else - { - // since it wasn't present during last calculation - m_resultCache.insert(AZStd::make_pair(repId, peerSet)); - } - } - - // Mark attributes (replicas) for removal that have not moved but a rule (clients) no longer sees it - for (auto& possiblyDirty : before) - { - ReplicaId repId = possiblyDirty.first; - - const auto foundInAfter = after.find(repId); - /* - * If the prior state was a replica A present on peer X: "A{X}", and now A should no longer be present on any peer: "A{}" - * then by the rules of InterestHandlers interacting with InterestManager, we should return in @m_resultCache the following: - * - * A{} - indicating that replica A must be removed all peers. - * - * On the next pass, the prior state would be: "A{}" and the current state would be "A{}" as well. At that point, we have - * already sent the update to remove A from X, so @m_resultCache should no longer mention A at all. - */ - if (foundInAfter == after.end() && !possiblyDirty.second.empty() /* "not A{}" see the above comment */) - { - m_resultCache.insert(AZStd::make_pair(repId, InterestPeerSet())); - } - } - -#if defined(INTERNAL_DEBUG_PROXIMITY) - m_resultCache.PrintMatchResult("changes"); -#endif - - } - - bool ProximityInterestHandler::HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another) - { - if (one.size() != another.size()) - { - return false; - } - - for (auto& peerFromOne : one) - { - if (another.find(peerFromOne) == another.end()) - { - return false; - } - } - - // Safe to assume it's the same sets since all entries are unique in a peer sets - return true; - } - - void ProximityInterestHandler::Update() - { - InterestMatchResult newResult; - - UpdateInternal(newResult); - ProduceChanges(m_lastResult, newResult); - - m_lastResult = std::move(newResult); - ClearDirtyState(); - } - - void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager) - { - AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager); - AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n"); - AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n"); - m_im = manager; - m_rm = m_im->GetReplicaManager(); - m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this); - - auto replica = Replica::CreateReplica("ProximityInterestHandlerRules"); - m_rulesReplica = CreateAndAttachReplicaChunk(replica); - m_rm->AddPrimary(replica); - } - - void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager) - { - (void)manager; - AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im); - AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n"); - m_rulesReplica = nullptr; - m_im = nullptr; - m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)); - m_rm = nullptr; - - for (auto& chunk : m_peerChunks) - { - chunk.second->m_interestHandler = nullptr; - } - m_peerChunks.clear(); - - ClearDirtyState(); - DestroyAll(); - - m_resultCache.clear(); - } - - void ProximityInterestHandler::DestroyAll() - { - for (ProximityInterestRule* rule : m_localRules) - { - FreeRule(rule); - } - m_localRules.clear(); - - for (ProximityInterestAttribute* attr : m_attributes) - { - FreeAttribute(attr); - } - m_attributes.clear(); - - for (auto& removedRule : m_removedRules) - { - FreeRule(removedRule); - } - m_removedRules.clear(); - - for (auto& removedAttribute : m_removedAttributes) - { - FreeAttribute(removedAttribute); - } - m_removedAttributes.clear(); - } - - /////////////////////////////////////////////////////////////////////////// - ProximityInterestHandler::~ProximityInterestHandler() - { - /* - * If a handler was registered with a InterestManager, then InterestManager ought to have called OnRulesHandlerUnregistered - * but this is a safety pre-caution. - */ - DestroyAll(); - } - - SpatialIndex::SpatialIndex() - { - m_tree.reset(aznew GridMate::BvDynamicTree()); - } - - void SpatialIndex::Remove(Node* node) - { - m_tree->Remove(node); - } - - void SpatialIndex::Update(Node* node) - { - m_tree->Update(node); - } - - SpatialIndex::Node* SpatialIndex::Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute) - { - return m_tree->Insert(get, attribute); - } - - void SpatialIndex::Query(const AZ::Aabb& shape, NodeCollector& nodes) - { - m_tree->collideTV(m_tree->GetRoot(), shape, nodes); - } -} diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h b/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h deleted file mode 100644 index 6659d44f44..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h +++ /dev/null @@ -1,314 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#ifndef GM_REPLICA_PROXIMITYINTERESTHANDLER_H -#define GM_REPLICA_PROXIMITYINTERESTHANDLER_H - -#include -#include -#include -#include -#include - -#include -#include - -namespace GridMate -{ - class ProximityInterestHandler; - class ProximityInterestAttribute; - - /* - * Base interest - */ - class ProximityInterest - { - friend class ProximityInterestHandler; - - public: - const AZ::Aabb& Get() const { return m_bbox; } - - protected: - explicit ProximityInterest(ProximityInterestHandler* handler); - - ProximityInterestHandler* m_handler; - AZ::Aabb m_bbox; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity rule - */ - class ProximityInterestRule - : public InterestRule - , public ProximityInterest - { - friend class ProximityInterestHandler; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestRule); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId) - : InterestRule(peerId, netId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - class SpatialIndex - { - public: - typedef Internal::DynamicTreeNode Node; - - class NodeCollector - { - typedef AZStd::vector Type; - - public: - void Process(const Internal::DynamicTreeNode* node) - { - m_nodes.push_back(reinterpret_cast(node->m_data)); - } - - const Type& GetNodes() const - { - return m_nodes; - } - - private: - Type m_nodes; - }; - - SpatialIndex(); - ~SpatialIndex() = default; - - AZ_FORCE_INLINE void Remove(Node* node); - AZ_FORCE_INLINE void Update(Node* node); - AZ_FORCE_INLINE Node* Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void Query(const AZ::Aabb& get, NodeCollector& nodes); - - private: - AZStd::unique_ptr m_tree; - }; - - /* - * Proximity attribute - */ - class ProximityInterestAttribute - : public InterestAttribute - , public ProximityInterest - { - friend class ProximityInterestHandler; - template friend class InterestPtr; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestAttribute); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId) - : InterestAttribute(repId) - , ProximityInterest(handler) - , m_worldNode(nullptr) - {} - - void Destroy(); - - void SetNode(SpatialIndex::Node* node) { m_worldNode = node; } - SpatialIndex::Node* GetNode() const { return m_worldNode; } - SpatialIndex::Node* m_worldNode; ///< non-owning pointer - }; - /////////////////////////////////////////////////////////////////////////// - - class ProximityInterestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(ProximityInterestChunk); - - // ReplicaChunk - typedef AZStd::intrusive_ptr Ptr; - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override { return true; } - static const char* GetChunkName() { return "ProximityInterestChunk"; } - - ProximityInterestChunk() - : AddRuleRpc("AddRule") - , RemoveRuleRpc("RemoveRule") - , UpdateRuleRpc("UpdateRule") - , AddRuleForPeerRpc("AddRuleForPeerRpc") - , m_interestHandler(nullptr) - { - } - - void OnReplicaActivate(const ReplicaContext& rc) override; - void OnReplicaDeactivate(const ReplicaContext& rc) override; - - bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx); - bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&); - bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&); - bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&); - - Rpc, RpcArg>::BindInterface AddRuleRpc; - Rpc>::BindInterface RemoveRuleRpc; - Rpc, RpcArg>::BindInterface UpdateRuleRpc; - - Rpc, RpcArg, RpcArg>::BindInterface AddRuleForPeerRpc; - - unordered_map m_rules; - ProximityInterestHandler* m_interestHandler; - }; - - /* - * Rules handler - */ - class ProximityInterestHandler - : public BaseRulesHandler - { - friend class ProximityInterestRule; - friend class ProximityInterestAttribute; - friend class ProximityInterestChunk; - - public: - - typedef unordered_set AttributeSet; - typedef unordered_set RuleSet; - - GM_CLASS_ALLOCATOR(ProximityInterestHandler); - - ProximityInterestHandler(); - ~ProximityInterestHandler(); - - /* - * Creates new proximity rule and binds it to the peer. - * Note: the lifetime of the created rule is tied to the lifetime of this handler. - */ - ProximityInterestRule::Ptr CreateRule(PeerId peerId); - - /* - * Creates new proximity attribute and binds it to the replica. - * Note: the lifetime of the created attribute is tied to the lifetime of this handler. - */ - ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId); - - // Calculates rules and attributes matches - void Update() override; - - // Returns last recalculated results - const InterestMatchResult& GetLastResult() override; - - // Returns the manager it's bound to - InterestManager* GetManager() override { return m_im; } - - // Rules that this handler is aware of - const RuleSet& GetLocalRules() const { return m_localRules; } - - private: - - // BaseRulesHandler - void OnRulesHandlerRegistered(InterestManager* manager) override; - void OnRulesHandlerUnregistered(InterestManager* manager) override; - - void DestroyRule(ProximityInterestRule* rule); - void FreeRule(ProximityInterestRule* rule); - void UpdateRule(ProximityInterestRule* rule); - - void DestroyAttribute(ProximityInterestAttribute* attrib); - void FreeAttribute(ProximityInterestAttribute* attrib); - void UpdateAttribute(ProximityInterestAttribute* attrib); - - void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - - RuleNetworkId GetNewRuleNetId(); - - ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId); - - void DestroyAll(); - - InterestManager* m_im; - ReplicaManager* m_rm; - - AZ::u32 m_lastRuleNetId; - - unordered_map m_peerChunks; - - RuleSet m_localRules; - RuleSet m_removedRules; - RuleSet m_dirtyRules; - - AttributeSet m_attributes; - AttributeSet m_removedAttributes; - AttributeSet m_dirtyAttributes; - - ProximityInterestChunk* m_rulesReplica; - - // collection of all known attributes - AZStd::unique_ptr m_attributeWorld; - - InterestMatchResult m_resultCache; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // internal processing helpers - AZ_FORCE_INLINE RuleSet& GetAffectedRules(); - AZ_FORCE_INLINE void GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes); - AZ_FORCE_INLINE void ClearDirtyState(); - - AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void RemoveFromSpatialStructure(ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule); - - void UpdateInternal(InterestMatchResult& result); - void CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result); - void MarkAttributesDirtyInRule(ProximityInterestRule* rule); - - static bool HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another); - void ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after); - - InterestMatchResult m_lastResult; - /////////////////////////////////////////////////////////////////////////////////////////////////// - }; - /////////////////////////////////////////////////////////////////////////// -} - -#endif // GM_REPLICA_PROXIMITYINTERESTHANDLER_H diff --git a/Code/Framework/GridMate/GridMate/gridmate_files.cmake b/Code/Framework/GridMate/GridMate/gridmate_files.cmake index 7cc65ff69a..615ce53027 100644 --- a/Code/Framework/GridMate/GridMate/gridmate_files.cmake +++ b/Code/Framework/GridMate/GridMate/gridmate_files.cmake @@ -100,14 +100,10 @@ set(FILES Replica/Tasks/ReplicaPriorityPolicy.h Replica/Interest/BitmaskInterestHandler.cpp Replica/Interest/BitmaskInterestHandler.h - Replica/Interest/ProximityInterestHandler.cpp - Replica/Interest/ProximityInterestHandler.h Replica/Interest/InterestDefs.h Replica/Interest/InterestManager.cpp Replica/Interest/InterestManager.h Replica/Interest/InterestQueryResult.h - Replica/Interest/BvDynamicTree.cpp - Replica/Interest/BvDynamicTree.h Replica/Interest/RulesHandler.h Serialize/Buffer.cpp Serialize/Buffer.h diff --git a/Code/Framework/GridMate/Tests/Interest.cpp b/Code/Framework/GridMate/Tests/Interest.cpp deleted file mode 100644 index c449c92083..0000000000 --- a/Code/Framework/GridMate/Tests/Interest.cpp +++ /dev/null @@ -1,1823 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ -#include "Tests.h" -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace GridMate; - -// An easy switch to use an old brute force ProximityInterestHandler for performance comparison. -#if 0 -namespace GridMate -{ - /* - * Base interest - */ - class ProximityInterest - { - friend class ProximityInterestHandler; - - public: - const AZ::Aabb& Get() const { return m_bbox; } - - protected: - explicit ProximityInterest(ProximityInterestHandler* handler); - - ProximityInterestHandler* m_handler; - AZ::Aabb m_bbox; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity rule - */ - class ProximityInterestRule - : public InterestRule - , public ProximityInterest - { - friend class ProximityInterestHandler; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestRule); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId) - : InterestRule(peerId, netId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity attribute - */ - class ProximityInterestAttribute - : public InterestAttribute - , public ProximityInterest - { - friend class ProximityInterestHandler; - template friend class InterestPtr; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestAttribute); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId) - : InterestAttribute(repId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Rules handler - */ - class ProximityInterestHandler - : public BaseRulesHandler - { - friend class ProximityInterestRule; - friend class ProximityInterestAttribute; - friend class ProximityInterestChunk; - - public: - - typedef unordered_set AttributeSet; - typedef unordered_set RuleSet; - - GM_CLASS_ALLOCATOR(ProximityInterestHandler); - - ProximityInterestHandler(); - - // Creates new proximity rule and binds it to the peer - ProximityInterestRule::Ptr CreateRule(PeerId peerId); - - // Creates new proximity attribute and binds it to the replica - ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId); - - // Calculates rules and attributes matches - void Update() override; - - // Returns last recalculated results - const InterestMatchResult& GetLastResult() override; - - // Returns manager its bound with - InterestManager* GetManager() override { return m_im; } - - const RuleSet& GetLocalRules() { return m_localRules; } - - private: - void UpdateInternal(InterestMatchResult& result); - - // BaseRulesHandler - void OnRulesHandlerRegistered(InterestManager* manager) override; - void OnRulesHandlerUnregistered(InterestManager* manager) override; - - void DestroyRule(ProximityInterestRule* rule); - void FreeRule(ProximityInterestRule* rule); - void UpdateRule(ProximityInterestRule* rule); - - void DestroyAttribute(ProximityInterestAttribute* attrib); - void FreeAttribute(ProximityInterestAttribute* attrib); - void UpdateAttribute(ProximityInterestAttribute* attrib); - - - void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - - RuleNetworkId GetNewRuleNetId(); - - ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId); - - InterestManager* m_im; - ReplicaManager* m_rm; - - AZ::u32 m_lastRuleNetId; - - unordered_map m_peerChunks; - RuleSet m_localRules; - - AttributeSet m_attributes; - RuleSet m_rules; - - ProximityInterestChunk* m_rulesReplica; - - InterestMatchResult m_resultCache; - }; - /////////////////////////////////////////////////////////////////////////// - - class ProximityInterestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(ProximityInterestChunk); - - // ReplicaChunk - typedef AZStd::intrusive_ptr Ptr; - bool IsReplicaMigratable() override { return false; } - static const char* GetChunkName() { return "ProximityInterestChunk"; } - bool IsBroadcast() { return true; } - /////////////////////////////////////////////////////////////////////////// - - - ProximityInterestChunk() - : m_interestHandler(nullptr) - , AddRuleRpc("AddRule") - , RemoveRuleRpc("RemoveRule") - , UpdateRuleRpc("UpdateRule") - , AddRuleForPeerRpc("AddRuleForPeerRpc") - { - - } - - void OnReplicaActivate(const ReplicaContext& rc) override - { - m_interestHandler = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4))); - AZ_Assert(m_interestHandler, "No proximity interest handler in the user context"); - - if (m_interestHandler) - { - m_interestHandler->OnNewRulesChunk(this, rc.m_peer); - } - } - - void OnReplicaDeactivate(const ReplicaContext& rc) override - { - if (rc.m_peer && m_interestHandler) - { - m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer); - } - } - - bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx) - { - if (IsProxy()) - { - auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer); - rulePtr->Set(bbox); - m_rules.insert(AZStd::make_pair(netId, rulePtr)); - } - - return true; - } - - bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&) - { - if (IsProxy()) - { - m_rules.erase(netId); - } - - return true; - } - - bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&) - { - if (IsProxy()) - { - auto it = m_rules.find(netId); - if (it != m_rules.end()) - { - it->second->Set(bbox); - } - } - - return true; - } - - bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&) - { - ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId); - if (peerChunk) - { - auto it = peerChunk->m_rules.find(netId); - if (it == peerChunk->m_rules.end()) - { - auto rulePtr = m_interestHandler->CreateRule(peerId); - peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr)); - rulePtr->Set(bbox); - } - } - return false; - } - - Rpc, RpcArg>::BindInterface AddRuleRpc; - Rpc>::BindInterface RemoveRuleRpc; - Rpc, RpcArg>::BindInterface UpdateRuleRpc; - - Rpc, RpcArg, RpcArg>::BindInterface AddRuleForPeerRpc; - - ProximityInterestHandler* m_interestHandler; - unordered_map m_rules; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterest - */ - ProximityInterest::ProximityInterest(ProximityInterestHandler* handler) - : m_bbox(AZ::Aabb::CreateNull()) - , m_handler(handler) - { - AZ_Assert(m_handler, "Invalid interest handler"); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestRule - */ - void ProximityInterestRule::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateRule(this); - } - - void ProximityInterestRule::Destroy() - { - m_handler->DestroyRule(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestAttribute - */ - void ProximityInterestAttribute::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateAttribute(this); - } - - void ProximityInterestAttribute::Destroy() - { - m_handler->DestroyAttribute(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestHandler - */ - ProximityInterestHandler::ProximityInterestHandler() - : m_im(nullptr) - , m_rm(nullptr) - , m_lastRuleNetId(0) - , m_rulesReplica(nullptr) - { - if (!ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(ProximityInterestChunk::GetChunkName()))) - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - } - - ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId) - { - ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId()); - if (peerId == m_rm->GetLocalPeerId()) - { - m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get()); - m_localRules.insert(rulePtr); - } - - return rulePtr; - } - - void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule) - { - //TODO: should be pool-allocated - delete rule; - } - - void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId()); - } - - rule->m_bbox = AZ::Aabb::CreateNull(); - m_rules.insert(rule); - m_localRules.erase(rule); - } - - void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule) - { - if (rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get()); - } - - m_rules.insert(rule); - } - - ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId) - { - return aznew ProximityInterestAttribute(this, replicaId); - } - - void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib) - { - //TODO: should be pool-allocated - delete attrib; - } - - void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib) - { - attrib->m_bbox = AZ::Aabb::CreateNull(); - m_attributes.insert(attrib); - } - - void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib) - { - m_attributes.insert(attrib); - } - - void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - if (chunk != m_rulesReplica) // non-local - { - m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk)); - - for (auto& rule : m_localRules) - { - chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get()); - } - } - } - - void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - (void)chunk; - m_peerChunks.erase(peer->GetId()); - } - - RuleNetworkId ProximityInterestHandler::GetNewRuleNetId() - { - ++m_lastRuleNetId; - return m_rulesReplica->GetReplicaId() | (static_cast(m_lastRuleNetId) << 32); - } - - ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId) - { - auto it = m_peerChunks.find(peerId); - if (it == m_peerChunks.end()) - { - return nullptr; - } - else - { - return it->second; - } - } - - const InterestMatchResult& ProximityInterestHandler::GetLastResult() - { - return m_resultCache; - } - - void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result) - { - ////////////////////////////////////////////// - // just recalculating the whole state for now - for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ) - { - ProximityInterestAttribute* attr = *attrIt; - - auto resultIt = result.insert(attr->GetReplicaId()); - for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); ++ruleIt) - { - ProximityInterestRule* rule = *ruleIt; - if (rule->m_bbox.Overlaps(attr->m_bbox)) - { - resultIt.first->second.insert(rule->GetPeerId()); - } - } - - if ((*attrIt)->IsDeleted()) - { - attrIt = m_attributes.erase(attrIt); - delete attr; - } - else - { - ++attrIt; - } - } - - for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); ) - { - ProximityInterestRule* rule = *ruleIt; - - if (rule->IsDeleted()) - { - ruleIt = m_rules.erase(ruleIt); - delete rule; - } - else - { - ++ruleIt; - } - } - ////////////////////////////////////////////// - } - - void ProximityInterestHandler::Update() - { - m_resultCache.clear(); - - UpdateInternal(m_resultCache); - } - - void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager) - { - AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager); - AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n"); - AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n"); - m_im = manager; - m_rm = m_im->GetReplicaManager(); - m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this); - - auto replica = Replica::CreateReplica("ProximityInterestHandlerRules"); - m_rulesReplica = CreateAndAttachReplicaChunk(replica); - m_rm->AddPrimary(replica); - } - - void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager) - { - (void)manager; - AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im); - AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n"); - m_rulesReplica = nullptr; - m_im = nullptr; - m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)); - m_rm = nullptr; - - for (auto& chunk : m_peerChunks) - { - chunk.second->m_interestHandler = nullptr; - } - m_peerChunks.clear(); - m_localRules.clear(); - - for (ProximityInterestRule* rule : m_rules) - { - delete rule; - } - - for (ProximityInterestAttribute* attr : m_attributes) - { - delete attr; - } - - m_attributes.clear(); - m_rules.clear(); - - m_resultCache.clear(); - } - /////////////////////////////////////////////////////////////////////////// -} -#else -// Optimized spatial handler. -#include "GridMate/Replica/Interest/ProximityInterestHandler.h" -#endif - -namespace UnitTest { - -/* - * Helper class to capture performance of various Interest Managers - */ -class PerfForInterestManager -{ -public: - void Reset(); - - void PreUpdate(); - void PostUpdate(); - - AZ::u32 GetTotalFrames() const; - float GetAverageFrame() const; - float GetWorstFrame() const; - float GetBestFrame() const; - -private: - AZ::Debug::Timer m_timer; - AZ::u32 m_frameCount = 0; - float m_totalUpdateTime = 0.f; - float m_fastestFrame = 100.f; - float m_slowestFrame = 0.f; -}; - -PerfForInterestManager g_PerfIM = PerfForInterestManager(); -PerfForInterestManager g_PerfUpdatingAttributes = PerfForInterestManager(); - -/* -* Utility function to tick the replica manager -*/ -static void UpdateReplicas(ReplicaManager* replicaManager, InterestManager* interestManager) -{ - if (interestManager) - { - // Measuring time it takes to execute an update. - g_PerfIM.PreUpdate(); - interestManager->Update(); - g_PerfIM.PostUpdate(); - } - - if (replicaManager) - { - replicaManager->Unmarshal(); - replicaManager->UpdateFromReplicas(); - replicaManager->UpdateReplicas(); - replicaManager->Marshal(); - } -} - -class Integ_InterestTest - : public GridMateMPTestFixture -{ - /////////////////////////////////////////////////////////////////// - class InterestTestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(InterestTestChunk); - - InterestTestChunk() - : m_data("Data", 0) - , m_bitmaskAttributeData("BitmaskAttributeData") - , m_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////// - typedef AZStd::intrusive_ptr Ptr; - static const char* GetChunkName() { return "InterestTestChunk"; } - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override - { - return false; - } - /////////////////////////////////////////////////////////////////// - - void OnReplicaActivate(const ReplicaContext& rc) override - { - AZ_Printf("GridMate", "InterestTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - - BitmaskInterestHandler* ih = static_cast(rc.m_rm->GetUserContext(AZ_CRC("BitmaskInterestHandler", 0x5bf5d75b))); - if (ih) - { - m_attribute = ih->CreateAttribute(GetReplicaId()); - m_attribute->Set(m_bitmaskAttributeData.Get()); - } - } - - void OnReplicaDeactivate(const ReplicaContext& rc) override - { - AZ_Printf("GridMate", "InterestTestChunk::OnReplicaDeactivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - - m_attribute = nullptr; - } - - void BitmaskHandler(const InterestBitmask& bitmask, const TimeContext&) - { - if (m_attribute) - { - m_attribute->Set(bitmask); - } - } - - DataSet m_data; - DataSet::BindInterface m_bitmaskAttributeData; - BitmaskInterestAttribute::Ptr m_attribute; - }; - /////////////////////////////////////////////////////////////////// - - class TestPeerInfo - : public SessionEventBus::Handler - { - public: - TestPeerInfo() - : m_gridMate(nullptr) - , m_lanSearch(nullptr) - , m_session(nullptr) - , m_im(nullptr) - , m_bitmaskHandler(nullptr) - , m_num(0) - { - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - - void CreateTestReplica() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_bitmaskHandler = aznew BitmaskInterestHandler(); - m_im->RegisterHandler(m_bitmaskHandler); - - m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(1 << m_num); - - auto r = Replica::CreateReplica("InterestTestReplica"); - m_replica = CreateAndAttachReplicaChunk(r); - - // Initializing attribute - // Shifing all by one - peer0 will recv from peer1, peer2 will recv from peer2, peer2 will recv from peer0 - unsigned i = (m_num + 2) % Integ_InterestTest::k_numMachines; - m_replica->m_data.Set(m_num); - m_replica->m_bitmaskAttributeData.Set(1 << i); - - m_session->GetReplicaMgr()->AddPrimary(r); - } - - void UpdateAttribute() - { - // Shifing all by one - peer0 will recv from peer2, peer1 will recv from peer0, peer2 will recv from peer1 - unsigned i = (m_num + 1) % Integ_InterestTest::k_numMachines; - m_replica->m_bitmaskAttributeData.Set(1 << i); - m_replica->m_attribute->Set(1 << i); - } - - void DeleteAttribute() - { - m_replica->m_attribute = nullptr; - } - - void UpdateRule() - { - m_rule->Set(0xffff); - } - - void DeleteRule() - { - m_rule = nullptr; - } - - void CreateRule() - { - m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(0xffff); - } - - void OnSessionCreated(GridSession* session) override - { - m_session = session; - if (m_session->IsHost()) - { - CreateTestReplica(); - } - } - - void OnSessionJoined(GridSession* session) override - { - m_session = session; - CreateTestReplica(); - } - - void OnSessionDelete(GridSession* session) override - { - if (session == m_session) - { - m_rule = nullptr; - m_session = nullptr; - m_im->UnregisterHandler(m_bitmaskHandler); - delete m_bitmaskHandler; - delete m_im; - m_im = nullptr; - m_bitmaskHandler = nullptr; - } - } - - void OnSessionError(GridSession* session, const string& errorMsg) override - { - (void)session; - (void)errorMsg; - AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str()); - } - - IGridMate* m_gridMate; - GridSearch* m_lanSearch; - GridSession* m_session; - InterestManager* m_im; - BitmaskInterestHandler* m_bitmaskHandler; - - BitmaskInterestRule::Ptr m_rule; - unsigned m_num; - InterestTestChunk::Ptr m_replica; - }; - -public: - Integ_InterestTest() - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - - ////////////////////////////////////////////////////////////////////////// - // Create all grid mates - m_peers[0].m_gridMate = m_gridMate; - m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate); - m_peers[0].m_num = 0; - for (int i = 1; i < k_numMachines; ++i) - { - GridMateDesc desc; - m_peers[i].m_gridMate = GridMateCreate(desc); - AZ_TEST_ASSERT(m_peers[i].m_gridMate); - - m_peers[i].m_num = i; - m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate); - } - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - // start the multiplayer service (session mgr, extra allocator, etc.) - StartGridMateService(m_peers[i].m_gridMate, SessionServiceDesc()); - AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr); - } - } - virtual ~Integ_InterestTest() - { - StopGridMateService(m_peers[0].m_gridMate); - - for (int i = 1; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].SessionEventBus::Handler::BusDisconnect(); - GridMateDestroy(m_peers[i].m_gridMate); - } - } - - // this will stop the first IGridMate which owns the memory allocators. - m_peers[0].SessionEventBus::Handler::BusDisconnect(); - } - - void run() - { - TestCarrierDesc carrierDesc; - carrierDesc.m_enableDisconnectDetection = false;// true; - carrierDesc.m_threadUpdateTimeMS = 10; - carrierDesc.m_familyType = Driver::BSD_AF_INET; - - - LANSessionParams sp; - sp.m_topology = ST_PEER_TO_PEER; - sp.m_numPublicSlots = 64; - sp.m_port = k_hostPort; - EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc); - m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - int listenPort = k_hostPort; - for (int i = 0; i < k_numMachines; ++i) - { - if (i == k_host) - { - continue; - } - - LANSearchParams searchParams; - searchParams.m_serverPort = k_hostPort; - searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports - searchParams.m_familyType = Driver::BSD_AF_INET; - EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams); - } - - - static const int maxNumUpdates = 300; - int numUpdates = 0; - TimeStamp time = AZStd::chrono::system_clock::now(); - - while (numUpdates <= maxNumUpdates) - { - if (numUpdates == 100) - { - // Checking everybody received only one replica: - // peer0 -> rep1, peer1 -> rep2, peer2 -> rep0 - for (int i = 0; i < k_numMachines; ++i) - { - ReplicaId repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId(); - auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repRecv != nullptr); - - repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId(); - auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repNotRecv == nullptr); - - // rotating mask left - m_peers[i].UpdateAttribute(); - } - } - - if (numUpdates == 150) - { - // Checking everybody received only one replica: - // peer0 -> rep2, peer1 -> rep0, peer2 -> rep1 - for (int i = 0; i < k_numMachines; ++i) - { - ReplicaId repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId(); - auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repRecv != nullptr); - - repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId(); - auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repNotRecv == nullptr); - - // setting rules to accept all replicas - m_peers[i].UpdateRule(); - } - } - - if (numUpdates == 200) - { - // Checking everybody received all replicas - for (int i = 0; i < k_numMachines; ++i) - { - for (int j = 0; j < k_numMachines; ++j) - { - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep); - } - - // Deleting all attributes - m_peers[i].DeleteAttribute(); - } - } - - if (numUpdates == 250) - { - // Checking everybody lost all replicas (except primary) - for (int i = 0; i < k_numMachines; ++i) - { - for (int j = 0; j < k_numMachines; ++j) - { - if (i == j) - { - continue; - } - - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep == nullptr); - } - - // deleting all rules - m_peers[i].DeleteRule(); - } - } - - ////////////////////////////////////////////////////////////////////////// - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].m_gridMate->Update(); - if (m_peers[i].m_session) - { - UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im); - } - } - } - Update(); - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone()) - { - AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1); - JoinParams jp; - EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc); - m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - m_peers[i].m_lanSearch->Release(); - m_peers[i].m_lanSearch = nullptr; - } - } - - ////////////////////////////////////////////////////////////////////////// - // Debug Info - TimeStamp now = AZStd::chrono::system_clock::now(); - if (AZStd::chrono::milliseconds(now - time).count() > 1000) - { - time = now; - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_session == nullptr) - { - continue; - } - - if (m_peers[i].m_session->IsHost()) - { - AZ_Printf("GridMate", "------ Host %d ------\n", i); - } - else - { - AZ_Printf("GridMate", "------ Client %d ------\n", i); - } - - AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime()); - for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember) - { - GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember); - AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no"); - } - AZ_Printf("GridMate", "\n"); - } - } - ////////////////////////////////////////////////////////////////////////// - - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30)); - numUpdates++; - } - } - - static const int k_numMachines = 3; - static const int k_host = 0; - static const int k_hostPort = 5450; - - TestPeerInfo m_peers[k_numMachines]; -}; - -/* - * Testing worse case performance of thousands of replicas and a few peers where all replicas/attributes change every frame - * and peers/rules change every frame as well. - */ -class LargeWorldTest - : public GridMateMPTestFixture -{ - /////////////////////////////////////////////////////////////////// - class LargeWorldTestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(LargeWorldTestChunk); - - LargeWorldTestChunk() - : m_data("Data", 0) - , m_proximityAttributeData("LargeWorldAttributeData") - , m_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////// - typedef AZStd::intrusive_ptr Ptr; - static const char* GetChunkName() { return "LargeWorldTestChunk"; } - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override - { - return false; - } - /////////////////////////////////////////////////////////////////// - - void OnReplicaActivate(const ReplicaContext& rc) override - { - /*if (!IsPrimary())*/ - /*{ - AZ_Printf("GridMate", "LargeWorldTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - }*/ - - if (ProximityInterestHandler* ih = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)))) - { - m_attribute = ih->CreateAttribute(GetReplicaId()); - m_attribute->Set(m_proximityAttributeData.Get()); - } - } - - void OnReplicaDeactivate(const ReplicaContext& /*rc*/) override - { - m_attribute = nullptr; - } - - void ProximityHandler(const AZ::Aabb& bounds, const TimeContext&) - { - if (m_attribute) - { - m_attribute->Set(bounds); - } - } - - DataSet m_data; - DataSet::BindInterface m_proximityAttributeData; - ProximityInterestAttribute::Ptr m_attribute; - }; - /////////////////////////////////////////////////////////////////// - - struct LargeWorldParams - { - AZ::u32 index = 0; - - const float commonSize = 50; - const AZ::Aabb box = AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateZero(), - AZ::Vector3::CreateOne() * commonSize); - const float commonStep = commonSize + 1; - }; - - static LargeWorldParams& GetWorldParams() - { - static LargeWorldParams worldParams; - return worldParams; - } - - /* - * Create a chain of boxes in spaces along X axis - */ - static AZ::Aabb CreateNextRuleSpace() - { - auto offset = GetWorldParams().commonStep * aznumeric_cast(GetWorldParams().index); - - float min[] = { offset, 0, 0 }; - float max[] = { - GetWorldParams().commonSize + offset, - GetWorldParams().commonSize, - GetWorldParams().commonSize }; - - auto bounds = AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateFromFloat3(min), - AZ::Vector3::CreateFromFloat3(max)); - - GetWorldParams().index++; - return bounds; - } - - class LargeWorldTestPeerInfo - : public SessionEventBus::Handler - { - public: - LargeWorldTestPeerInfo() - : m_gridMate(nullptr) - , m_lanSearch(nullptr) - , m_session(nullptr) - , m_im(nullptr) - , m_proximityHandler(nullptr) - , m_num(0) - { - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - - ~LargeWorldTestPeerInfo() - { - SessionEventBus::Handler::BusDisconnect(); - } - - void CreateHostRuleHandler() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_proximityHandler = aznew ProximityInterestHandler(); - m_im->RegisterHandler(m_proximityHandler); - - m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(AZ::Aabb::CreateNull()); // host rule doesn't matter in this test - } - - void CreateRuleHandler() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_proximityHandler = aznew ProximityInterestHandler(); - m_im->RegisterHandler(m_proximityHandler); - - m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(CreateNextRuleSpace()); - } - - void CreateTestReplica(const AZ::Aabb& bounds) - { - auto r = Replica::CreateReplica("LargeWorldTestReplica"); - auto replica = CreateAndAttachReplicaChunk(r); - - // Initializing attribute - replica->m_data.Set(m_num); - replica->m_proximityAttributeData.Set(bounds); - - m_replicas.push_back(replica); - - m_session->GetReplicaMgr()->AddPrimary(r); - } - - void PopulateWorld() - { - AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() starting...\n"); - - const float worldSizeInBoxes = 50; - const auto oneBox = AZ::Vector3::CreateOne(); - const auto thickness = 1; - for (float dx = 0; dx < worldSizeInBoxes; ++dx) - { - for (float dy = 0; dy < thickness; ++dy) - { - for (float dz = 0; dz < thickness; ++dz) - { - auto aabb = AZ::Aabb::CreateFromMinMax( - AZ::Vector3(50 * dx + 5, dy, dz), - AZ::Vector3(50 * dx + 5, dy, dz) + oneBox); - - CreateTestReplica(aabb); - } - } - } - - AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() ... DONE\n"); - } - - void UpdateAttribute(LargeWorldTestChunk::Ptr replica) - { - if (replica && replica->m_attribute) - { - auto sameValue = replica->m_attribute->Get(); - - replica->m_proximityAttributeData.Set(sameValue); - replica->m_attribute->Set(sameValue); - } - } - - void UpdateRule() - { - // just make it dirty for now - if (m_rule) - { - auto sameValue = m_rule->Get(); - m_rule->Set(sameValue); - } - } - - void DeleteRule() - { - m_rule = nullptr; - } - - void OnSessionCreated(GridSession* session) override - { - m_session = session; - if (m_session->IsHost()) - { - CreateHostRuleHandler(); - PopulateWorld(); - } - } - - void OnSessionJoined(GridSession* session) override - { - m_session = session; - CreateRuleHandler(); - } - - void OnSessionDelete(GridSession* session) override - { - if (session == m_session) - { - m_rule = nullptr; - m_session = nullptr; - m_im->UnregisterHandler(m_proximityHandler); - delete m_proximityHandler; - delete m_im; - m_im = nullptr; - m_proximityHandler = nullptr; - } - } - - void OnSessionError(GridSession* session, const string& errorMsg) override - { - (void)session; - (void)errorMsg; - AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str()); - } - - IGridMate* m_gridMate; - GridSearch* m_lanSearch; - GridSession* m_session; - InterestManager* m_im; - ProximityInterestHandler* m_proximityHandler; - - ProximityInterestRule::Ptr m_rule; - unsigned m_num; - - AZStd::vector m_replicas; - }; - -public: - LargeWorldTest() : UnitTest::GridMateMPTestFixture(500u * 1024u * 1024u) // 500Mb - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - - ////////////////////////////////////////////////////////////////////////// - // Create all grid mates - m_peers[0].m_gridMate = m_gridMate; - m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate); - m_peers[0].m_num = 0; - for (int i = 1; i < k_numMachines; ++i) - { - GridMateDesc desc; - m_peers[i].m_gridMate = GridMateCreate(desc); - AZ_TEST_ASSERT(m_peers[i].m_gridMate); - - m_peers[i].m_num = i; - m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate); - } - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - // start the multiplayer service (session mgr, extra allocator, etc.) - StartGridMateService(m_peers[i].m_gridMate, SessionServiceDesc()); - AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr); - } - } - virtual ~LargeWorldTest() - { - StopGridMateService(m_peers[0].m_gridMate); - - for (int i = 1; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].SessionEventBus::Handler::BusDisconnect(); - GridMateDestroy(m_peers[i].m_gridMate); - } - } - - // this will stop the first IGridMate which owns the memory allocators. - m_peers[0].SessionEventBus::Handler::BusDisconnect(); - } - - void run() - { - g_PerfIM.Reset(); - g_PerfUpdatingAttributes.Reset(); - - TestCarrierDesc carrierDesc; - carrierDesc.m_enableDisconnectDetection = false; - carrierDesc.m_threadUpdateTimeMS = 10; - carrierDesc.m_familyType = Driver::BSD_AF_INET; - - LANSessionParams sp; - sp.m_topology = ST_PEER_TO_PEER; - sp.m_numPublicSlots = 64; - sp.m_port = k_hostPort; - EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc); - m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - int listenPort = k_hostPort; - for (int i = 0; i < k_numMachines; ++i) - { - if (i == k_host) - { - continue; - } - - LANSearchParams searchParams; - searchParams.m_serverPort = k_hostPort; - searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports - searchParams.m_familyType = Driver::BSD_AF_INET; - EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams); - } - - - static const int maxNumUpdates = 300; - int numUpdates = 0; - TimeStamp time = AZStd::chrono::system_clock::now(); - - while (numUpdates <= maxNumUpdates) - { - g_PerfUpdatingAttributes.PreUpdate(); - for (LargeWorldTestChunk::Ptr replica : m_peers[0].m_replicas) - { - m_peers[0].UpdateAttribute(replica); - } - g_PerfUpdatingAttributes.PostUpdate(); - - for (auto peer : m_peers) - { - peer.UpdateRule(); - } - - //if (numUpdates == 100) - //{ - // // check how many replicas each client got - // for (AZ::u32 i = 1; i < k_numMachines; ++i) - // { - // AZ::u32 count = 0; - // for (auto& replica : m_peers[0].m_replicas) - // { - // ReplicaId repId = replica->GetReplicaId(); - // if (auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId)) - // { - // count++; - // } - // } - - // const AZ::Aabb& bounds = m_peers[i].m_rule->Get(); - - // AZ_Printf("GridMate", "Session %s Members: %d Bounds: %f.%f.%f-%f.%f.%f Replicas: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), - // static_cast(bounds.GetMin().GetX()), - // static_cast(bounds.GetMin().GetY()), - // static_cast(bounds.GetMin().GetZ()), - // static_cast(bounds.GetMax().GetX()), - // static_cast(bounds.GetMax().GetY()), - // static_cast(bounds.GetMax().GetZ()), count); - - // AZ_Assert(count == 1, "Should have at least some replicas to start with"); - // } - //} - - if (numUpdates == 200) - { - // Deleting all attributes - for (auto& replica : m_peers[0].m_replicas) - { - replica->m_attribute = nullptr; - } - } - - if (numUpdates == 250) - { - // Checking everybody lost all replicas (except primary) - for (int i = 0; i < k_numMachines; ++i) - { - /*for (int j = 0; j < k_numMachines; ++j) - { - if (i == j) - { - continue; - } - - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep == nullptr); - }*/ - - // deleting all rules - m_peers[i].DeleteRule(); - } - } - - ////////////////////////////////////////////////////////////////////////// - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].m_gridMate->Update(); - if (m_peers[i].m_session) - { - UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im); - } - } - } - Update(); - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone()) - { - AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1); - JoinParams jp; - EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc); - m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - m_peers[i].m_lanSearch->Release(); - m_peers[i].m_lanSearch = nullptr; - } - } - - ////////////////////////////////////////////////////////////////////////// - // Debug Info - TimeStamp now = AZStd::chrono::system_clock::now(); - if (AZStd::chrono::milliseconds(now - time).count() > 1000) - { - time = now; - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_session == nullptr) - { - continue; - } - - if (m_peers[i].m_session->IsHost()) - { - AZ_Printf("GridMate", "------ Host %d ------\n", i); - } - else - { - AZ_Printf("GridMate", "------ Client %d ------\n", i); - } - - AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime()); - for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember) - { - GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember); - AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no"); - } - AZ_Printf("GridMate", "\n"); - } - } - ////////////////////////////////////////////////////////////////////////// - - //AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30)); - numUpdates++; - } - - auto averageFrame = g_PerfIM.GetAverageFrame(); - auto bestFrame = g_PerfIM.GetBestFrame(); - auto worstFrame = g_PerfIM.GetWorstFrame(); - auto frames = g_PerfIM.GetTotalFrames(); - AZ_Printf("GridMate", "Interest manager performance: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n", - averageFrame, frames, bestFrame, worstFrame); - - AZ_Printf("GridMate", "Updating attributes: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n", - g_PerfUpdatingAttributes.GetAverageFrame(), - g_PerfUpdatingAttributes.GetTotalFrames(), - g_PerfUpdatingAttributes.GetBestFrame(), - g_PerfUpdatingAttributes.GetWorstFrame()); - } - - static const int k_numMachines = 3; - static const int k_host = 0; - static const int k_hostPort = 5450; - - LargeWorldTestPeerInfo m_peers[k_numMachines]; -}; - -void PerfForInterestManager::Reset() -{ - m_frameCount = 0; - m_totalUpdateTime = 0; - m_slowestFrame = 0; - m_fastestFrame = 100.f; -} - -void PerfForInterestManager::PreUpdate() -{ - m_timer.Stamp(); -} - -void PerfForInterestManager::PostUpdate() -{ - auto frameTime = m_timer.StampAndGetDeltaTimeInSeconds(); - m_totalUpdateTime += frameTime; - m_frameCount++; - - m_slowestFrame = AZStd::max(m_slowestFrame, frameTime); - m_fastestFrame = AZStd::min(m_fastestFrame, frameTime); -} - -AZ::u32 PerfForInterestManager::GetTotalFrames() const -{ - return m_frameCount; -} - -float PerfForInterestManager::GetAverageFrame() const -{ - if (m_frameCount > 0) - { - return m_totalUpdateTime / m_frameCount; - } - - return 0; -} - -float PerfForInterestManager::GetWorstFrame() const -{ - return m_slowestFrame; -} - -float PerfForInterestManager::GetBestFrame() const -{ - return m_fastestFrame; -} - -class ProximityHandlerTests - : public GridMateMPTestFixture -{ -public: - struct xyz - { - float x, y, z; - }; - - static AZ::Aabb CreateBox(xyz min, float size) - { - return AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateFromFloat3(&min.x), - AZ::Vector3::CreateFromFloat3(&min.x) + AZ::Vector3::CreateOne() * size); - } - - static void run() - { - SimpleFirstUpdate(); - SecondUpdateAfterNoChanges(); - SimpleOutsideOfRule(); - AttributeMovingOutsideOfRule(); - RuleMovingAndAttributeIsOut(); - RuleDestroyed(); - AttributeDestroyed(); - } - - static void RuleMovingAndAttributeIsOut() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "1st"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - - // now move the attribute outside of the rule - rule1->Set(CreateBox({ 1000, 0, 0 }, 100)); - - handler->Update(); - results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "2nd"); - - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void AttributeMovingOutsideOfRule() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "1st"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - - // now move the attribute outside of the rule - attribute1->Set(CreateBox({ -1000, 0, 0 }, 10)); - - handler->Update(); - results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "2nd"); - - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void SimpleFirstUpdate() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - } - - static void SecondUpdateAfterNoChanges() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results.size() == 0); - } - - static void SimpleOutsideOfRule() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ -1000, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void RuleDestroyed() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - { - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 1); - } - - // rule1 should have been destroyed by now - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::PrintMatchResult(results, "last"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void AttributeDestroyed() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - { - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 1); - } - - // attribute1 should have been destroyed by now, but it will show up once to remove it from affected peers - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - results.PrintMatchResult("last"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - - // and now attribute1 should not show up in the changes - handler->Update(); - results = handler->GetLastResult(); - results.PrintMatchResult("last"); - - AZ_TEST_ASSERT(results.size() == 0); - } -}; - -}; // namespace UnitTest - -GM_TEST_SUITE(InterestSuite) - GM_TEST(Integ_InterestTest); -#if AZ_TRAIT_GRIDMATE_TEST_EXCLUDE_LARGEWORLDTEST != 0 - GM_TEST(LargeWorldTest); -#endif - GM_TEST(ProximityHandlerTests); -GM_TEST_SUITE_END() diff --git a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake index a8b1dbd120..90ccbbf9a9 100644 --- a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake +++ b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake @@ -24,5 +24,4 @@ set(FILES StreamSocketDriverTests.cpp CarrierStreamSocketDriverTests.cpp Carrier.cpp - Interest.cpp ) diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index 430fe5ca4b..992d39c418 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -123,6 +123,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) BUILD_DEPENDENCIES PRIVATE Gem::Multiplayer.Builders.Static + RUNTIME_DEPENDENCIES + Gem::Multiplayer.Editor ) ly_add_target( diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index 31884d4c94..ea966907d1 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -130,6 +130,7 @@ namespace Multiplayer // Instance container for net entities AZStd::unique_ptr networkInstance(aznew Instance()); + networkInstance->SetTemplateSourcePath(AZ::IO::PathView(uniqueName)); // Create an asset for our future network spawnable: this allows us to put references to the asset in the components AZ::Data::Asset networkSpawnableAsset;