コード例 #1
0
ファイル: CodeGenTests.cs プロジェクト: impoetk/bepuphysics2
        unsafe static void TestQuickInlining()
        {
            {
                var pool    = new PassthroughArrayPool <double>();
                var intPool = new PassthroughArrayPool <int>();
                QuickSet <double, Array <double>, Array <int>, PrimitiveComparer <double> > .Create(pool, intPool, 2, 3, out var set);

                set.AddUnsafely(5);
                var item = set[0];
                Console.WriteLine($"Managed Item: {item}");

                var comparer = default(PrimitiveComparer <double>);
                var hash     = comparer.Hash(ref item);

                Console.WriteLine($"Hash: {hash}");
            }

            {
                var pool = new BufferPool().SpecializeFor <int>();
                QuickSet <int, Buffer <int>, Buffer <int>, PrimitiveComparer <int> > .Create(pool, pool, 2, 3, out var set);

                set.AddUnsafely(5);
                var item = set[0];
                pool.Raw.Clear();
            }
        }
コード例 #2
0
        void AddDigit(ref double value, ref double multiplier, ref PassthroughArrayPool <char> pool)
        {
            var digit = (int)((value * multiplier) % 10);

            characters.Add(GetCharForDigit(digit), pool);
            value      -= digit / multiplier;
            multiplier *= 10;
        }
コード例 #3
0
        public TextBuilder Append(double value, int decimalCount)
        {
            //This is a bit of a throwaway implementation and is far from the fastest or numerically best implementation,
            //but it is fairly simple and it doesn't matter very much.

            const double minimumDoubleMagnitude = 2.22507385850720138309023271733240406421921598046233e-308;
            bool         negative = value < 0;

            if (negative)
            {
                value = -value;
            }
            var pool = new PassthroughArrayPool <char>();

            if (value <= minimumDoubleMagnitude)
            {
                //Don't bother with signed zeroes.
                characters.Add('0', pool);
                return(this);
            }
            if (negative)
            {
                characters.Add('-', pool);
            }
            value = Math.Round(value, decimalCount);
            var place      = (int)Math.Floor(Math.Log10(value));
            var multiplier = Math.Pow(0.1, place);
            var epsilon    = Math.Pow(0.1, decimalCount);

            for (int i = place; i >= 0; --i)
            {
                AddDigit(ref value, ref multiplier, ref pool);
            }
            if (value > epsilon)
            {
                characters.Add('.', pool);
                for (int i = -1; i > place; --i)
                {
                    characters.Add('0', pool);
                }
                do
                {
                    AddDigit(ref value, ref multiplier, ref pool);
                } while (value > epsilon);
            }
            return(this);
        }
コード例 #4
0
ファイル: Input.cs プロジェクト: impoetk/bepuphysics2
        public Input(Window window)
        {
            this.window             = window.window;
            this.window.KeyDown    += KeyDown;
            this.window.KeyUp      += KeyUp;
            this.window.MouseDown  += MouseDown;
            this.window.MouseUp    += MouseUp;
            this.window.MouseWheel += MouseWheel;

            var keyPool         = new PassthroughArrayPool <Key>();
            var mouseButtonPool = new PassthroughArrayPool <MouseButton>();
            var intPool         = new PassthroughArrayPool <int>();

            MouseButtonSet.Create(mouseButtonPool, intPool, 3, 3, out anyDownedButtons);
            MouseButtonSet.Create(mouseButtonPool, intPool, 3, 3, out downedButtons);
            MouseButtonSet.Create(mouseButtonPool, intPool, 3, 3, out previousDownedButtons);
            KeySet.Create(keyPool, intPool, 3, 3, out anyDownedKeys);
            KeySet.Create(keyPool, intPool, 3, 3, out downedKeys);
            KeySet.Create(keyPool, intPool, 3, 3, out previousDownedKeys);
        }
コード例 #5
0
ファイル: Input.cs プロジェクト: impoetk/bepuphysics2
        public void End()
        {
            anyDownedKeys.Clear();
            anyDownedButtons.Clear();
            previousDownedKeys.Clear();
            previousDownedButtons.Clear();
            var keyPool         = new PassthroughArrayPool <Key>();
            var mouseButtonPool = new PassthroughArrayPool <MouseButton>();
            var intPool         = new PassthroughArrayPool <int>();

            for (int i = 0; i < downedKeys.Count; ++i)
            {
                previousDownedKeys.Add(downedKeys[i], keyPool, intPool);
            }
            for (int i = 0; i < downedButtons.Count; ++i)
            {
                previousDownedButtons.Add(downedButtons[i], mouseButtonPool, intPool);
            }
            ScrolledDown = 0;
            ScrolledUp   = 0;
        }
コード例 #6
0
ファイル: Input.cs プロジェクト: netdebug/bepuphysics2
        public Input(Window window)
        {
            this.window             = window.window;
            this.window.KeyDown    += KeyDown;
            this.window.KeyUp      += KeyUp;
            this.window.MouseDown  += MouseDown;
            this.window.MouseUp    += MouseUp;
            this.window.MouseWheel += MouseWheel;
            this.window.KeyPress   += KeyPress;
            var keyPool         = new PassthroughArrayPool <Key>();
            var mouseButtonPool = new PassthroughArrayPool <MouseButton>();
            var intPool         = new PassthroughArrayPool <int>();

            MouseButtonSet.Create(mouseButtonPool, intPool, 3, 3, out anyDownedButtons);
            MouseButtonSet.Create(mouseButtonPool, intPool, 3, 3, out downedButtons);
            MouseButtonSet.Create(mouseButtonPool, intPool, 3, 3, out previousDownedButtons);
            KeySet.Create(keyPool, intPool, 3, 3, out anyDownedKeys);
            KeySet.Create(keyPool, intPool, 3, 3, out downedKeys);
            KeySet.Create(keyPool, intPool, 3, 3, out previousDownedKeys);
            QuickList <char, Array <char> > .Create(new PassthroughArrayPool <char>(), 32, out TypedCharacters);
        }
コード例 #7
0
        public static void TestChurnStability()
        {
            var   allocator = new Allocator(2048);
            var   random    = new Random(5);
            ulong idCounter = 0;
            var   pool      = new PassthroughArrayPool <ulong>();

            QuickList <ulong, Array <ulong> > .Create(pool, 8, out var allocatedIds);

            QuickList <ulong, Array <ulong> > .Create(pool, 8, out var unallocatedIds);

            for (int i = 0; i < 512; ++i)
            {
                long start;
                var  id = idCounter++;
                //allocator.ValidatePointers();
                if (allocator.Allocate(id, 1 + random.Next(5), out start))
                {
                    allocatedIds.Add(id, pool);
                }
                else
                {
                    unallocatedIds.Add(id, pool);
                }
                //allocator.ValidatePointers();
            }
            for (int timestepIndex = 0; timestepIndex < 100000; ++timestepIndex)
            {
                //First add and remove a bunch randomly.
                for (int i = random.Next(Math.Min(allocatedIds.Count, 15)); i >= 0; --i)
                {
                    var indexToRemove = random.Next(allocatedIds.Count);
                    //allocator.ValidatePointers();
                    var deallocated = allocator.Deallocate(allocatedIds[indexToRemove]);
                    Debug.Assert(deallocated);
                    //allocator.ValidatePointers();
                    unallocatedIds.Add(allocatedIds[indexToRemove], pool);
                    allocatedIds.FastRemoveAt(indexToRemove);
                }
                for (int i = random.Next(Math.Min(unallocatedIds.Count, 15)); i >= 0; --i)
                {
                    var indexToAllocate = random.Next(unallocatedIds.Count);
                    //allocator.ValidatePointers();
                    if (allocator.Allocate(unallocatedIds[indexToAllocate], random.Next(3), out long start))
                    {
                        //allocator.ValidatePointers();
                        allocatedIds.Add(unallocatedIds[indexToAllocate], pool);
                        unallocatedIds.FastRemoveAt(indexToAllocate);
                    }
                    //allocator.ValidatePointers();
                }
                //Check to ensure that everything's still coherent.
                for (int i = 0; i < allocatedIds.Count; ++i)
                {
                    Debug.Assert(allocator.Contains(allocatedIds[i]));
                }
                for (int i = 0; i < unallocatedIds.Count; ++i)
                {
                    Debug.Assert(!allocator.Contains(unallocatedIds[i]));
                }
            }
            //Wind it down.
            for (int i = 0; i < allocatedIds.Count; ++i)
            {
                var deallocated = allocator.Deallocate(allocatedIds[i]);
                Debug.Assert(deallocated);
            }
            //Confirm cleanup.
            for (int i = 0; i < allocatedIds.Count; ++i)
            {
                Debug.Assert(!allocator.Contains(allocatedIds[i]));
            }
            for (int i = 0; i < unallocatedIds.Count; ++i)
            {
                Debug.Assert(!allocator.Contains(unallocatedIds[i]));
            }
        }
コード例 #8
0
        public unsafe static FontContent Build(Stream fontDataStream)
        {
            var faceBytes = new byte[fontDataStream.Length];

            fontDataStream.Read(faceBytes, 0, faceBytes.Length);
            using (var library = new Library())
            {
                using (var face = new Face(library, faceBytes, 0))
                {
                    //Collect glyph boundings information.
                    face.SetPixelSizes(FontSizeInPixels, FontSizeInPixels);
                    var sortedCharacterSet  = new char[characterSet.Length];
                    var sortedCharacterData = new CharacterData[characterSet.Length];

                    for (int i = 0; i < characterSet.Length; ++i)
                    {
                        sortedCharacterSet[i] = characterSet[i];
                        face.LoadGlyph(face.GetCharIndex(characterSet[i]), LoadFlags.Default, LoadTarget.Normal);
                        ref var characterData = ref sortedCharacterData[i];
                        characterData.SourceSpan.X = face.Glyph.Metrics.Width.ToInt32();
                        characterData.SourceSpan.Y = face.Glyph.Metrics.Height.ToInt32();

                        characterData.Bearing.X = face.Glyph.Metrics.HorizontalBearingX.ToInt32();
                        characterData.Bearing.Y = face.Glyph.Metrics.HorizontalBearingY.ToInt32();

                        characterData.Advance = face.Glyph.Metrics.HorizontalAdvance.ToInt32();
                    }

                    //Next, allocate space in the atlas for each character.
                    //Sort the characters by height, and then scan from one side of the atlas to the other placing characters.
                    //Once the other side is reached, flip directions and repeat. Continue until no more characters remain.
                    Array.Sort(sortedCharacterData, sortedCharacterSet, new CharacterHeightComparer());

                    const int padding    = 1 << MipLevels;
                    var       characters = new Dictionary <char, CharacterData>();
                    var       packer     = new FontPacker(AtlasWidth, MipLevels, padding, characterSet.Length);
                    for (int i = 0; i < sortedCharacterSet.Length; ++i)
                    {
                        //The packer class handles the placement logic and sets the SourceMinimum in the character data, too.
                        packer.Add(ref sortedCharacterData[i]);
                    }

                    //Now that every glyph has been positioned within the sheet, we can actually rasterize the glyph alphas into a bitmap proto-atlas.
                    //We're building the rasterized set sequentially first so we don't have to worry about threading issues in the underlying library.
                    var rasterizedAlphas = new Texture2DContent(AtlasWidth, packer.Height, 1, 1);
                    for (int i = 0; i < sortedCharacterSet.Length; ++i)
                    {
                        //Rasterize the glyph.
                        var character = sortedCharacterSet[i];
                        face.LoadGlyph(face.GetCharIndex(character), LoadFlags.Default, LoadTarget.Normal);
                        face.Glyph.RenderGlyph(RenderMode.Normal);

                        //Copy the alphas into the pixel alpha buffer at the appropriate position.
                        int glyphWidth  = face.Glyph.Bitmap.Width;
                        int glyphHeight = face.Glyph.Bitmap.Rows;
                        var glyphBuffer = (byte *)face.Glyph.Bitmap.Buffer;


                        Int2 location;
                        location.X = sortedCharacterData[i].SourceMinimum.X;
                        location.Y = sortedCharacterData[i].SourceMinimum.Y;
                        for (int glyphRow = 0; glyphRow < glyphHeight; ++glyphRow)
                        {
                            Unsafe.CopyBlockUnaligned(
                                ref rasterizedAlphas.Data[rasterizedAlphas.GetRowOffsetForMip0(glyphRow + location.Y) + location.X],
                                ref glyphBuffer[glyphRow * glyphWidth], (uint)glyphWidth);
                        }
                    }

                    //Preallocate memory for full single precision float version of the atlas. This will be used as scratch memory (admittedly, more than is necessary)
                    //which will be encoded into the final single byte representation after the mips are calculated. The full precision stage makes the mips a little more accurate.
                    var preciseAtlas = new Texture2DContent(AtlasWidth, packer.Height, MipLevels, 4);
                    var atlas        = new Texture2DContent(AtlasWidth, packer.Height, MipLevels, 1);
                    //Compute the distances for every character-covered texel in the atlas.
                    var atlasData   = atlas.Pin();
                    var preciseData = (float *)preciseAtlas.Pin();
                    var alphaData   = rasterizedAlphas.Pin();
                    var pool        = new PassthroughArrayPool <Int2>();
                    //for (int i = 0; i < sortedCharacterData.Length; ++i)
                    Parallel.For(0, sortedCharacterData.Length, i =>
                    {
                        //Note that the padding around characters should also have its distances filled in. That way, the less detailed mips can pull from useful data.
                        ref var charData = ref sortedCharacterData[i];

                        var min       = new Int2(charData.SourceMinimum.X, charData.SourceMinimum.Y);
                        var max       = new Int2(charData.SourceMinimum.X + charData.SourceSpan.X, charData.SourceMinimum.Y + charData.SourceSpan.Y);
                        var paddedMin = new Int2(min.X - padding, min.Y - padding);
                        var paddedMax = new Int2(max.X + padding, max.Y + padding);
                        //Initialize every character texel to max distance. The following BFS only ever reduces distances, so it has to start high.
                        var maxDistance = Math.Max(AtlasWidth, packer.Height);
                        for (int rowIndex = paddedMin.Y; rowIndex < paddedMax.Y; ++rowIndex)
                        {
                            var rowOffset    = preciseAtlas.GetRowOffsetForMip0(rowIndex);
                            var distancesRow = preciseData + rowOffset;
                            for (int columnIndex = paddedMin.X; columnIndex < paddedMax.X; ++columnIndex)
                            {
                                distancesRow[columnIndex] = maxDistance;
                            }
                        }


                        //Scan the alphas. Add border texels of the glyph to the point set. We collect both the nonzero alpha outline and the 'negative space' zero alpha outline.
                        //While scanning distances, nonzero alpha texels will look for the shortest distance to a zero alpha texel, while zero alpha texels will look for the shortest
                        //distance to a nonzero alpha texel.
                        var glyphOutline      = new List <Int2>((max.X - min.X) * (max.Y - min.Y));
                        int coverageThreshold = 127;
                        for (int rowIndex = min.Y; rowIndex < max.Y; ++rowIndex)
                        {
                            //Alphas and atlas have same dimensions, so sharing row offset is safe.
                            Debug.Assert(padding > 0, "This assumes at least one padding; no boundary checking is performed on the alpha accesses.");
                            var rowOffset  = preciseAtlas.GetRowOffsetForMip0(rowIndex);
                            var alphasRow0 = alphaData + rowOffset - preciseAtlas.Width;
                            var alphasRow1 = alphaData + rowOffset;
                            var alphasRow2 = alphaData + rowOffset + preciseAtlas.Width;
                            for (int columnIndex = min.X; columnIndex < max.X; ++columnIndex)
                            {
                                if (alphasRow1[columnIndex] >= coverageThreshold)
                                {
                                    //This texel is considered covered.
                                    //Only add this to the point set if there is at least one adjacent uncovered texel.
                                    //If there isn't an uncovered texel next to this one, then it can't be on the surface.
                                    if (alphasRow0[columnIndex] < coverageThreshold ||
                                        alphasRow1[columnIndex - 1] < coverageThreshold ||
                                        alphasRow1[columnIndex + 1] < coverageThreshold ||
                                        alphasRow2[columnIndex] < coverageThreshold)
                                    {
                                        Int2 texelCoordinates;
                                        texelCoordinates.X = columnIndex;
                                        texelCoordinates.Y = rowIndex;
                                        glyphOutline.Add(texelCoordinates);
                                    }
                                }
                            }
                        }

                        //For every texel in the character's region, scan the glyph point set for the nearest texel.
                        //Cache the largest distance as we go so that we can maximize precision within this character.
                        float largestDistanceMagnitude = 0;
                        for (int rowIndex = paddedMin.Y; rowIndex < paddedMax.Y; ++rowIndex)
                        {
                            var rowOffset    = preciseAtlas.GetRowOffsetForMip0(rowIndex); //Same dimensions; can be shared.
                            var distancesRow = preciseData + rowOffset;
                            var alphasRow    = alphaData + rowOffset;
                            for (int columnIndex = paddedMin.X; columnIndex < paddedMax.X; ++columnIndex)
                            {
                                //This is an uncovered texel. Look for a glyph outline.
                                float lowestDistance = float.MaxValue;
                                for (int pointIndex = 0; pointIndex < glyphOutline.Count; ++pointIndex)
                                {
                                    var point             = glyphOutline[pointIndex];
                                    var offsetX           = point.X - columnIndex;
                                    var offsetY           = point.Y - rowIndex;
                                    var candidateDistance = (float)Math.Sqrt(offsetX * offsetX + offsetY * offsetY);
                                    if (candidateDistance < lowestDistance)
                                    {
                                        lowestDistance = candidateDistance;
                                    }
                                }
                                //If it's uncovered, use a positive distance. If it's covered, use a negative distance.
                                distancesRow[columnIndex] = alphasRow[columnIndex] < coverageThreshold ? lowestDistance : -lowestDistance;
                                if (lowestDistance > largestDistanceMagnitude)
                                {
                                    largestDistanceMagnitude = lowestDistance;
                                }
                            }
                        }

                        //Build the mips. We already have all the data in cache on this core; 256KiB L2 can easily hold the processing context of a 128x128 glyph.
                        //(Though worrying about performance in the content builder too much is pretty silly. We aren't going to be building fonts often.)
                        //Note that we aligned and padded each glyph during packing. For a given texel in mip(n), the four parent texels in mip(n-1) can be safely sampled.
                        for (int mipLevel = 1; mipLevel < preciseAtlas.MipLevels; ++mipLevel)
                        {
                            var mipMin = new Int2(paddedMin.X >> mipLevel, paddedMin.Y >> mipLevel);
                            var mipMax = new Int2(paddedMax.X >> mipLevel, paddedMax.Y >> mipLevel);

                            //Yes, these do some redundant calculations, but no it doesn't matter.
                            var parentMipStart    = preciseData + preciseAtlas.GetMipStartIndex(mipLevel - 1);
                            var parentMipRowPitch = preciseAtlas.GetRowPitch(mipLevel - 1);
                            var mipStart          = preciseData + preciseAtlas.GetMipStartIndex(mipLevel);
                            var mipRowPitch       = preciseAtlas.GetRowPitch(mipLevel);
                            for (int mipRowIndex = mipMin.Y; mipRowIndex < mipMax.Y; ++mipRowIndex)
                            {
                                var mipRow         = mipStart + mipRowIndex * mipRowPitch;
                                var parentRowIndex = mipRowIndex << 1;
                                var parentMipRow0  = parentMipStart + parentRowIndex * parentMipRowPitch;
                                var parentMipRow1  = parentMipStart + (parentRowIndex + 1) * parentMipRowPitch;
                                for (int mipColumnIndex = mipMin.X; mipColumnIndex < mipMax.X; ++mipColumnIndex)
                                {
                                    var parentMipColumnIndex0 = mipColumnIndex << 1;
                                    var parentMipColumnIndex1 = parentMipColumnIndex0 + 1;
                                    mipRow[mipColumnIndex]    = 0.25f * (
                                        parentMipRow0[parentMipColumnIndex0] + parentMipRow0[parentMipColumnIndex1] +
                                        parentMipRow1[parentMipColumnIndex0] + parentMipRow1[parentMipColumnIndex1]);
                                }
                            }
                        }

                        //Now that all mips have been filled, bake the data into the final single byte encoding.
                        //Use the largest absolute distance as the encoding multiplier to maximize precision.
                        charData.DistanceScale = largestDistanceMagnitude;
                        var encodingMultiplier = 1f / largestDistanceMagnitude;
                        for (int mipLevel = 0; mipLevel < atlas.MipLevels; ++mipLevel)
                        {
                            var mipMin = new Int2(paddedMin.X >> mipLevel, paddedMin.Y >> mipLevel);
                            var mipMax = new Int2(paddedMax.X >> mipLevel, paddedMax.Y >> mipLevel);

                            //Note signed bytes. We're building an R8_SNORM texture, not UNORM.
                            var encodedStart = (sbyte *)atlasData + atlas.GetMipStartIndex(mipLevel);
                            var preciseStart = preciseData + preciseAtlas.GetMipStartIndex(mipLevel);
                            var rowPitch     = atlas.GetRowPitch(mipLevel);
                            for (int rowIndex = mipMin.Y; rowIndex < mipMax.Y; ++rowIndex)
                            {
                                var preciseRow = preciseStart + rowIndex * rowPitch;
                                var encodedRow = encodedStart + rowIndex * rowPitch;
                                for (int columnIndex = mipMin.X; columnIndex < mipMax.X; ++columnIndex)
                                {
                                    encodedRow[columnIndex] = (sbyte)(127 * Math.Max(-1, Math.Min(1, encodingMultiplier * preciseRow[columnIndex])));
                                }
                            }
                        }
                    });