static void Subtest <TBodyBuilder, TConstraintBuilder, TConstraint>(TBodyBuilder bodyBuilder, TConstraintBuilder constraintBuilder,
                                                                            int width, int height, int length, int frameCount, IThreadDispatcher initializationThreadPool, StreamWriter writer)
            where TBodyBuilder : IBodyBuilder where TConstraintBuilder : IConstraintBuilder where TConstraint : IConstraintDescription <TConstraint>
        {
            const int testsPerVariant = 8;

            WriteLine(writer, $"{width}x{height}x{length} lattice, {frameCount} frames:");
            var timings = new SimulationTimeSamples[Environment.ProcessorCount];


            //for (int threadCount = 1; threadCount <= 1; ++threadCount)
            for (int threadCount = 1; threadCount <= Environment.ProcessorCount; ++threadCount)
            {
                var threadPool = new SimpleThreadDispatcher(threadCount);
                SimulationTimeSamples bestTimings = null;
                double bestTime = double.MaxValue;
                for (int i = 0; i < testsPerVariant; ++i)
                {
                    var candidateTimings = Solve <TBodyBuilder, TConstraintBuilder, TConstraint>(bodyBuilder, constraintBuilder, width, height, length,
                                                                                                 frameCount, threadCount, initializationThreadPool, threadPool);
                    var totalStats = candidateTimings.Simulation.ComputeStats();
                    WriteLine(writer,
                              $"{i} AVE: {Math.Round(1e3 * totalStats.Average, 2)}, " +
                              $"MIN: {Math.Round(1e3 * totalStats.Min, 2)}, " +
                              $"MAX: {Math.Round(1e3 * totalStats.Max, 2)}, " +
                              $"STD DEV: {Math.Round(1e3 * totalStats.StdDev, 3)}, ");
                    if (totalStats.Total < bestTime)
                    {
                        bestTime    = totalStats.Total;
                        bestTimings = candidateTimings;
                    }
                }

                WriteLine(writer, $"{threadCount}T: " +
                          $"{Math.Round(bestTime * 1e3, 2)}, " +
                          $"BOPT: {Math.Round(bestTimings.BodyOptimizer.ComputeStats().Total * 1e3, 2)}, " +
                          $"COPT: {Math.Round(bestTimings.ConstraintOptimizer.ComputeStats().Total * 1e3, 2)}, " +
                          $"BCMP: {Math.Round(bestTimings.BatchCompressor.ComputeStats().Total * 1e3, 2)}, " +
                          $"POIN: {Math.Round(bestTimings.PoseIntegrator.ComputeStats().Total * 1e3, 2)}, " +
                          $"SOLV: {Math.Round(bestTimings.Solver.ComputeStats().Total * 1e3, 2)}");
                timings[threadCount - 1] = bestTimings;
                threadPool.Dispose();
            }
            int fastestIndex = 0;

            for (int i = 1; i < timings.Length; ++i)
            {
                if (timings[i].Simulation.ComputeStats().Total < timings[fastestIndex].Simulation.ComputeStats().Total)
                {
                    fastestIndex = i;
                }
            }
            WriteLine(writer, $"Scaling: {timings[0].Simulation.ComputeStats().Total / timings[fastestIndex].Simulation.ComputeStats().Total}");
        }
        public static void Test()
        {
            var memoryStream             = new MemoryStream();
            var writer                   = new StreamWriter(memoryStream);
            var initializationThreadPool = new SimpleThreadDispatcher(Environment.ProcessorCount);


            var bodyBuilder       = new RegularGridWithKinematicBaseBuilder(new Vector3(1), new Vector3());
            var constraintBuilder = new ContactManifoldConstraintBuilder();

            //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 32, 32, 32, 8, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 26, 26, 26, 12, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 20, 20, 20, 20, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 16, 16, 16, 30, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 13, 13, 13, 45, initializationThreadPool, writer);
            Subtest <RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, Contact4>(bodyBuilder, constraintBuilder, 10, 10, 10, 700, initializationThreadPool, writer);

            //var bodyBuilder = new RegularGridWithKinematicBaseBuilder(new Vector3(1), new Vector3());
            //var constraintBuilder = new BallSocketConstraintBuilder();
            //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 32, 32, 32, 8, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 26, 26, 26, 12, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 20, 20, 20, 20, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 16, 16, 16, 30, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 13, 13, 13, 45, initializationThreadPool, writer);
            //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 10, 10, 10, 70, initializationThreadPool, writer);
            initializationThreadPool.Dispose();
            writer.Flush();
            var path = "log.txt";

            using (var stream = File.OpenWrite(path))
            {
                Console.WriteLine($"Writing results to path: {Path.GetFullPath(path)}");
                var bytes = memoryStream.ToArray();
                stream.Write(bytes, 0, bytes.Length);
            }
            Console.ReadKey();
        }
Exemple #3
0
        public static void Test()
        {
            const int width  = 32;
            const int height = 32;
            const int length = 32;

            SimulationSetup.BuildLattice(
                new RegularGridWithKinematicBaseBuilder(new Vector3(1), new Vector3()),
                new BallSocketConstraintBuilder(),
                width, height, length, out var simulation, out var bodyHandles, out var constraintHandles);

            var threadDispatcher = new SimpleThreadDispatcher(8);

            const float inverseDt      = 60f;
            const float dt             = 1 / inverseDt;
            const int   iterationCount = 8;
            const int   frameCount     = 128;

            simulation.Solver.IterationCount = iterationCount;

            simulation.PoseIntegrator.Gravity = new Vector3(0, -10, 0);

            var samples = new SimulationTimeSamples(frameCount);

            for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex)
            {
                var energyBefore = TestHelpers.GetBodyEnergyHeuristic(simulation.Bodies);

                //simulation.Timestep(dt);
                simulation.Timestep(dt, threadDispatcher);

                samples.RecordFrame(simulation);

                var energyAfter      = TestHelpers.GetBodyEnergyHeuristic(simulation.Bodies);
                int sampledBodyIndex = width;
                var samplePose       = simulation.Bodies.ActiveSet.Poses[sampledBodyIndex];
                var sampleVelocity   = simulation.Bodies.ActiveSet.Velocities[sampledBodyIndex];
                //for (int i =0; i < simulation.Bodies.BodyCount; ++i)
                //{
                //    simulation.Bodies.GetPose(simulation.Bodies.IndexToHandle[i], out var pose);
                //    simulation.Bodies.GetVelocity(simulation.Bodies.IndexToHandle[i], out var velocity);
                //    Console.WriteLine($"Sample {i} position: {pose.Position}, velocity: {velocity.Linear}");
                //}
                Console.WriteLine($"Sample {sampledBodyIndex} position: {samplePose.Position}, velocity: {sampleVelocity.Linear}");

                Console.WriteLine($"Body energy {frameIndex}: {energyAfter}, delta: {energyAfter - energyBefore}");
            }

            var multiplier = 1e3 / frameCount;

            Console.WriteLine($"Simulation time (ms):       {multiplier * samples.Simulation.ComputeStats().Total}");
            Console.WriteLine($"Body opt time (ms):         {multiplier * samples.BodyOptimizer.ComputeStats().Total}");
            Console.WriteLine($"Constraint opt time (ms):   {multiplier * samples.ConstraintOptimizer.ComputeStats().Total}");
            Console.WriteLine($"Batch compress time (ms):   {multiplier * samples.BatchCompressor.ComputeStats().Total}");
            Console.WriteLine($"Pose integrate time (ms):   {multiplier * samples.PoseIntegrator.ComputeStats().Total}");
            Console.WriteLine($"Solve time (ms):            {multiplier * samples.Solver.ComputeStats().Total}");


            threadDispatcher.Dispose();
            simulation.BufferPool.Clear();
        }
Exemple #4
0
        public static void Test()
        {
            var pool = new BufferPool();
            var tree = new Tree(pool, 128);

            const int leafCountAlongXAxis = 11;
            const int leafCountAlongYAxis = 13;
            const int leafCountAlongZAxis = 15;
            var       leafCount           = leafCountAlongXAxis * leafCountAlongYAxis * leafCountAlongZAxis;

            pool.Take <BoundingBox>(leafCount, out var leafBounds);

            const float boundsSpan    = 2;
            const float spanRange     = 2;
            const float boundsSpacing = 3;
            var         random        = new Random(5);

            for (int i = 0; i < leafCountAlongXAxis; ++i)
            {
                for (int j = 0; j < leafCountAlongYAxis; ++j)
                {
                    for (int k = 0; k < leafCountAlongZAxis; ++k)
                    {
                        var index = leafCountAlongXAxis * leafCountAlongYAxis * k + leafCountAlongXAxis * j + i;
                        leafBounds[index].Min = new Vector3(i, j, k) * boundsSpacing;
                        leafBounds[index].Max = leafBounds[index].Min + new Vector3(boundsSpan) +
                                                spanRange * new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
                    }
                }
            }

            var prebuiltCount = Math.Max(leafCount / 2, 1);

            tree.SweepBuild(pool, leafBounds.Slice(0, prebuiltCount));
            tree.Validate();


            for (int i = prebuiltCount; i < leafCount; ++i)
            {
                tree.Add(ref leafBounds[i], pool);
            }
            tree.Validate();

            pool.Take <int>(leafCount, out var handleToLeafIndex);
            pool.Take <int>(leafCount, out var leafIndexToHandle);
            for (int i = 0; i < leafCount; ++i)
            {
                handleToLeafIndex[i] = i;
                leafIndexToHandle[i] = i;
            }

            const int iterations = 100000;
            const int maximumChangesPerIteration = 20;

            var          threadDispatcher = new SimpleThreadDispatcher(Environment.ProcessorCount);
            var          refineContext    = new Tree.RefitAndRefineMultithreadedContext();
            var          selfTestContext  = new Tree.MultithreadedSelfTest <OverlapHandler>(pool);
            var          overlapHandlers  = new OverlapHandler[threadDispatcher.ThreadCount];
            Action <int> pairTestAction   = selfTestContext.PairTest;

            QuickList <int, Buffer <int> > .Create(pool.SpecializeFor <int>(), leafCount, out var removedLeafHandles);

            for (int i = 0; i < iterations; ++i)
            {
                var changeCount = random.Next(maximumChangesPerIteration);
                for (int j = 0; j <= changeCount; ++j)
                {
                    var addedFraction = tree.LeafCount / (float)leafCount;
                    if (random.NextDouble() < addedFraction)
                    {
                        //Remove a leaf.
                        var leafIndexToRemove = random.Next(tree.LeafCount);
                        var handleToRemove    = leafIndexToHandle[leafIndexToRemove];
                        var movedLeafIndex    = tree.RemoveAt(leafIndexToRemove);
                        if (movedLeafIndex >= 0)
                        {
                            var movedHandle = leafIndexToHandle[movedLeafIndex];
                            handleToLeafIndex[movedHandle]       = leafIndexToRemove;
                            leafIndexToHandle[leafIndexToRemove] = movedHandle;
                            leafIndexToHandle[movedLeafIndex]    = -1;
                        }
                        else
                        {
                            //The removed leaf was the last one. This leaf index is no longer associated with any existing leaf.
                            leafIndexToHandle[leafIndexToRemove] = -1;
                        }
                        handleToLeafIndex[handleToRemove] = -1;

                        removedLeafHandles.AddUnsafely(handleToRemove);

                        tree.Validate();
                    }
                    else
                    {
                        //Add a leaf.
                        var indexInRemovedList = random.Next(removedLeafHandles.Count);
                        var handleToAdd        = removedLeafHandles[indexInRemovedList];
                        removedLeafHandles.FastRemoveAt(indexInRemovedList);
                        var leafIndex = tree.Add(ref leafBounds[handleToAdd], pool);
                        leafIndexToHandle[leafIndex]   = handleToAdd;
                        handleToLeafIndex[handleToAdd] = leafIndex;

                        tree.Validate();
                    }
                }

                tree.Refit();
                tree.Validate();

                tree.RefitAndRefine(pool, i);
                tree.Validate();

                var handler = new OverlapHandler();
                tree.GetSelfOverlaps(ref handler);
                tree.Validate();

                refineContext.RefitAndRefine(ref tree, pool, threadDispatcher, i);
                tree.Validate();
                for (int k = 0; k < threadDispatcher.ThreadCount; ++k)
                {
                    overlapHandlers[k] = new OverlapHandler();
                }
                selfTestContext.PrepareJobs(ref tree, overlapHandlers, threadDispatcher.ThreadCount);
                threadDispatcher.DispatchWorkers(pairTestAction);
                selfTestContext.CompleteSelfTest();
                tree.Validate();

                if (i % 50 == 0)
                {
                    Console.WriteLine($"Cost: {tree.MeasureCostMetric()}");
                    Console.WriteLine($"Cache Quality: {tree.MeasureCacheQuality()}");
                    Console.WriteLine($"Overlap Count: {handler.OverlapCount}");
                }
            }

            threadDispatcher.Dispose();
            pool.Clear();
        }