Как использовать ID2D1SpriteBatch в SharpDX? - PullRequest
0 голосов
/ 11 марта 2019

Попытка использовать ID2D1SpriteBatch в direct2d для повышения производительности по сравнению с обычным DrawBitmap ().

Удалось установить его, но я получаю "Объект не был в правильном состоянии для обработки метода"когда я вызываю DeviceContext.EndDraw ().

Я могу заставить DeviceContext.DrawBitmap () работать (см. Закомментированный раздел).Попробовал все, что я могу придумать, чтобы привести контекст устройства в правильное состояние для обработки spritebatch, но не повезло.

Пытался сварить этот образец как можно дольше, но также не хотел пропустить ни одного шага простона тот случай, если это был виновник.

Есть идеи, как заставить его работать?

using SharpDX;
using _d2d = SharpDX.Direct2D1;
using _d3d = SharpDX.Direct3D;
using _d3d11 = SharpDX.Direct3D11;
using _dxgi = SharpDX.DXGI;
using _directWrite = SharpDX.DirectWrite;
using _wic = SharpDX.WIC;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Windows;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using SharpDX.IO;
using SharpDX.Mathematics.Interop;

namespace TestApp
{
    public class SpriteBatchIssue
    {
        [STAThread]
        static void Main(string[] args)
        {
            var app = new SpriteBatchIssue();
            app.Run();
        }

        bool isClosed = false;

        public void Run()
        {
            #region setup resources      
            var clientSize = new Size2(1000, 500);

            var mainForm = new RenderForm();
            mainForm.ClientSize = new System.Drawing.Size(
                clientSize.Width,
                clientSize.Height);

            mainForm.FormClosed += mainForm_FormClosed;

            var scDescription = new SwapChainDescription()
            {
                BufferCount = 1,
                ModeDescription =
                    new ModeDescription(
                        clientSize.Width,
                        clientSize.Height,
                        new Rational(60, 1),
                        Format.R8G8B8A8_UNorm),
                IsWindowed = true,
                OutputHandle = mainForm.Handle,
                SampleDescription = new SampleDescription(1, 0),
                SwapEffect = SwapEffect.Discard,
                Usage = Usage.RenderTargetOutput
            };

            // Create Device and SwapChain
            _d3d11.Device d3d11Device;
            SwapChain swapChain;
            _d3d11.Device.CreateWithSwapChain(
                DriverType.Hardware,
                DeviceCreationFlags.BgraSupport,
                new[] { _d3d.FeatureLevel.Level_12_1 },
                scDescription,
                out d3d11Device,
                out swapChain);

            // Ignore all windows events
            var dxgiFactory = swapChain.GetParent<_dxgi.Factory1>();
            dxgiFactory.MakeWindowAssociation(mainForm.Handle, WindowAssociationFlags.IgnoreAll);

            // New RenderTargetView from the backbuffer
            var backBuffer = Texture2D.FromSwapChain<Texture2D>(swapChain, 0);
            var backBufferView = new RenderTargetView(d3d11Device, backBuffer);

            var d2dFactory = new _d2d.Factory();
            var d2dFactory4 = d2dFactory.QueryInterface<_d2d.Factory4>();

            var dxgiDevice = d3d11Device.QueryInterface<_dxgi.Device>();
            var d2dDevice3 = new _d2d.Device3(d2dFactory4, dxgiDevice);
            var d2dDeviceContext3 = new _d2d.DeviceContext3(d2dDevice3, DeviceContextOptions.None);

            using (var surface = backBuffer.QueryInterface<Surface>())
            {
                var bmpProperties = new BitmapProperties1(
                    new PixelFormat(Format.R8G8B8A8_UNorm, _d2d.AlphaMode.Premultiplied),
                    dpiX: 96,
                    dpiY: 96,
                    bitmapOptions: BitmapOptions.Target | BitmapOptions.CannotDraw);

                var d2dTarget = new Bitmap1(
                    d2dDeviceContext3,
                    surface,
                    bmpProperties);

                d2dDeviceContext3.Target = d2dTarget;
            }
            #endregion

            #region setup drawing parameters
            var bmp = createD2DBitmap(@"C:\yourPath\yourImage.png", d2dDeviceContext3);

            var spriteBatch = new SpriteBatch(d2dDeviceContext3);
            var destinationRects = new RawRectangleF[1];
            destinationRects[0] = new RectangleF(100, 50, bmp.Size.Width, bmp.Size.Height);

            var sourceRects = new RawRectangle[1];
            sourceRects[0] = new RectangleF(0, 0, bmp.Size.Width, bmp.Size.Height);

            var colors = new RawColor4[1];
            colors[0] = Color.White;

            var transforms = new RawMatrix3x2[1];
            transforms[0] = Matrix3x2.Identity;
            #endregion

            #region mainLoop
            RenderLoop.Run(mainForm, () =>
            {
                if (isClosed)
                {
                    return;
                }

                d3d11Device.ImmediateContext.Rasterizer.SetViewport(new Viewport(0, 0, clientSize.Width, clientSize.Height));
                d3d11Device.ImmediateContext.OutputMerger.SetTargets(backBufferView);

                d2dDeviceContext3.BeginDraw();

                //this technique works
                //d2dDeviceContext3.DrawBitmap(
                //    bitmap: bmp,
                //    destinationRectangle: destinationRects[0],
                //    opacity: 1,
                //    interpolationMode: BitmapInterpolationMode.Linear,
                //    sourceRectangle: new RectangleF(0, 0, bmp.Size.Width, bmp.Size.Height));

                //this technique does not work
                spriteBatch.Clear();
                spriteBatch.AddSprites(
                    1,
                    destinationRects,
                    sourceRects,
                    colors,
                    transforms,
                    destinationRectanglesStride: 0, //0 stride because there is only 1 element
                    sourceRectanglesStride: 0, //i've also tried using Marshal.SizeOf() to get the stride, but i get the same error
                    colorsStride: 0,
                    transformsStride: 0);

                d2dDeviceContext3.DrawSpriteBatch(
                    spriteBatch: spriteBatch,
                    startIndex: 0,
                    spriteCount: 1,
                    bitmap: bmp,
                    interpolationMode: BitmapInterpolationMode.Linear,
                    spriteOptions: SpriteOptions.ClampToSourceRectangle);

                //when using the spritebatch technique, this throws exception:
                // "The object was not in the correct state to process the method"
                d2dDeviceContext3.EndDraw();

                //first param set to 1 would indicate waitVerticalBlanking
                swapChain.Present(0, PresentFlags.None);
            });
            #endregion
        }

        Bitmap createD2DBitmap(string filePath, _d2d.DeviceContext deviceContext)
        {
            var imagingFactory = new _wic.ImagingFactory();

            var fileStream = new NativeFileStream(
                filePath,
                NativeFileMode.Open,
                NativeFileAccess.Read);

            var bitmapDecoder = new _wic.BitmapDecoder(imagingFactory, fileStream, _wic.DecodeOptions.CacheOnDemand);
            var frame = bitmapDecoder.GetFrame(0);

            var converter = new _wic.FormatConverter(imagingFactory);
            converter.Initialize(frame, SharpDX.WIC.PixelFormat.Format32bppPRGBA);

            var newBitmap = SharpDX.Direct2D1.Bitmap1.FromWicBitmap(deviceContext, converter);

            return newBitmap;
        }

        void mainForm_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e)
        {
            isClosed = true;
        }
    }
}

1 Ответ

0 голосов
/ 21 марта 2019

Проблема заключалась в том, что вы не можете использовать для каждого примитивного сглаживания спрайтбэтч.Эта строка до того, как BeginDraw () исправила ее: d2dDeviceContext3.AntialiasMode = AntialiasMode.Aliased;

Также, наконец, узнал, как заставить работать слой отладки.Включите флаг отладки при создании вашего устройства (см. Комментарии ниже).Если это вызывает исключение, это, вероятно, потому что у вас нет правильной версии Windows SDK.Если вы используете Visual Studio, перейдите к установщику Visual Studio и измените установку, чтобы включить Windows SDK.

Далее вам нужно щелкнуть правой кнопкой мыши ваш проект-> свойства-> отладка (на левой панели) ->установите флажок «включить отладку собственного кода».После того, как я это сделал, в окно вывода была записана строка: «D2D DEBUG ERROR - DrawSpriteBatch требует, чтобы для режима сглаживания было установлено значение D2D1_ANTIALIAS_MODE_ALIASED.»

Что-то еще, что я узнал, но не имеет прямого отношения кответ, но стоит отметить: «Обратите внимание, что ComObject в SharpDX не удаляется финализатором .NET. Если COM-объект не освобождается при вызове Dispose () или ReleaseReference (), он не освобождает собственный объект и память, присоединенную кЭто."отсюда: http://sharpdx.org/wiki/usage/

Вот полный рабочий пример:

using SharpDX;
using _d2d = SharpDX.Direct2D1;
using _d3d = SharpDX.Direct3D;
using _d3d11 = SharpDX.Direct3D11;
using _dxgi = SharpDX.DXGI;
using _directWrite = SharpDX.DirectWrite;
using _wic = SharpDX.WIC;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Windows;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using SharpDX.IO;
using SharpDX.Mathematics.Interop;

namespace TestApp
{
    public class SpriteBatchIssue
    {
        [STAThread]
        static void Main(string[] args)
        {
            var app = new SpriteBatchIssue();
            app.Run();
        }

        #region Variables
        _d3d11.Device d3d11Device;
        SwapChain swapChain;
        _dxgi.Factory1 dxgiFactory;
        _d2d.Factory d2dFactory;
        _d2d.Factory4 d2dFactory4;

        _dxgi.Device dxgiDevice;
        _d2d.Device3 d2dDevice3;
        _d2d.DeviceContext3 d2dDeviceContext3;

        Bitmap1 sourceImage;
        SpriteBatch spriteBatch;
        Bitmap1 d2dTarget;
        #endregion

        ~SpriteBatchIssue()
        {
            safeDispose(ref d3d11Device);
            safeDispose(ref swapChain);
            safeDispose(ref dxgiFactory);
            safeDispose(ref d2dFactory);
            safeDispose(ref d2dFactory4);
            safeDispose(ref dxgiDevice);
            safeDispose(ref d2dDevice3);
            safeDispose(ref d2dDeviceContext3);
            safeDispose(ref sourceImage);
            safeDispose(ref spriteBatch);
            safeDispose(ref d2dTarget);
        }

        public void Run()
        {
            #region setup resources      
            var mainForm = new RenderForm();

            var scDescription = new SwapChainDescription()
            {
                BufferCount = 1,
                ModeDescription =
                    new ModeDescription(
                        0,
                        0,
                        new Rational(60, 1),
                        Format.R8G8B8A8_UNorm),
                IsWindowed = true,
                OutputHandle = mainForm.Handle,
                SampleDescription = new SampleDescription(1, 0),
                SwapEffect = SwapEffect.Discard,
                Usage = Usage.RenderTargetOutput
            };

            //DeviceCreationFlags.Debug flag below will show debug layer messages in your output window.
            //Need proper version of windows sdk for it to work, otherwise it will throw an exception.
            //You also need to right click your project->properties->debug (on the left panel)-> check "enable native code debugging"

            // Create Device and SwapChain
            _d3d11.Device.CreateWithSwapChain(
                DriverType.Hardware,
                DeviceCreationFlags.BgraSupport | DeviceCreationFlags.Debug, 
                new[] { _d3d.FeatureLevel.Level_12_1 },
                scDescription,
                out d3d11Device,
                out swapChain);

            // Ignore all windows events
            dxgiFactory = swapChain.GetParent<_dxgi.Factory1>();
            dxgiFactory.MakeWindowAssociation(mainForm.Handle, WindowAssociationFlags.IgnoreAll);

            d2dFactory = new _d2d.Factory();
            d2dFactory4 = d2dFactory.QueryInterface<_d2d.Factory4>();

            dxgiDevice = d3d11Device.QueryInterface<_dxgi.Device>();
            d2dDevice3 = new _d2d.Device3(d2dFactory4, dxgiDevice);
            d2dDeviceContext3 = new _d2d.DeviceContext3(d2dDevice3, DeviceContextOptions.None);
            #endregion

            #region create drawing input
            sourceImage = createD2DBitmap(@"yourFile.png", d2dDeviceContext3);

            spriteBatch = new SpriteBatch(d2dDeviceContext3);
            var destinationRects = new RawRectangleF[1];
            destinationRects[0] = new RectangleF(100, 50, sourceImage.Size.Width, sourceImage.Size.Height);

            var sourceRects = new RawRectangle[1];
            sourceRects[0] = new RectangleF(0, 0, sourceImage.Size.Width, sourceImage.Size.Height);
            #endregion

            #region mainLoop
            RenderLoop.Run(mainForm, () =>
            {
                if (d2dTarget != null)
                {
                    d2dTarget.Dispose();
                    d2dTarget = null;
                }

                using (var backBuffer = Texture2D.FromSwapChain<Texture2D>(swapChain, 0))
                {
                    using (var surface = backBuffer.QueryInterface<Surface>())
                    {
                        var bmpProperties = new BitmapProperties1(
                            new PixelFormat(Format.R8G8B8A8_UNorm, _d2d.AlphaMode.Premultiplied),
                            dpiX: 96,
                            dpiY: 96,
                            bitmapOptions: BitmapOptions.Target | BitmapOptions.CannotDraw);

                        d2dTarget = new Bitmap1(
                            d2dDeviceContext3,
                            surface,
                            bmpProperties);

                        d2dDeviceContext3.Target = d2dTarget;
                    }
                }

                //the key missing piece: cannot use per primitive antialiasing with spritebatch
                d2dDeviceContext3.AntialiasMode = AntialiasMode.Aliased;
                d2dDeviceContext3.BeginDraw();

                spriteBatch.Clear();
                spriteBatch.AddSprites(
                    1,
                    destinationRects,
                    sourceRects,
                    null,
                    null,
                    destinationRectanglesStride: 0, //0 stride because there is only 1 element
                    sourceRectanglesStride: 0,
                    colorsStride: 0,
                    transformsStride: 0);

                d2dDeviceContext3.DrawSpriteBatch(
                    spriteBatch: spriteBatch,
                    startIndex: 0,
                    spriteCount: 1,
                    bitmap: sourceImage,
                    interpolationMode: BitmapInterpolationMode.Linear,
                    spriteOptions: SpriteOptions.ClampToSourceRectangle);

                d2dDeviceContext3.EndDraw();

                //first param set to 1 would indicate waitVerticalBlanking
                swapChain.Present(0, PresentFlags.None);
            });
            #endregion
        }

        void safeDispose<T>(ref T disposable) where T : class, IDisposable
        {
            if (disposable != null)
            {
                disposable.Dispose();
                disposable = null;
            }
        }

        Bitmap1 createD2DBitmap(string filePath, _d2d.DeviceContext deviceContext)
        {
            var imagingFactory = new _wic.ImagingFactory();

            var fileStream = new NativeFileStream(
                filePath,
                NativeFileMode.Open,
                NativeFileAccess.Read);

            var bitmapDecoder = new _wic.BitmapDecoder(imagingFactory, fileStream, _wic.DecodeOptions.CacheOnDemand);
            var frame = bitmapDecoder.GetFrame(0);

            var converter = new _wic.FormatConverter(imagingFactory);
            converter.Initialize(frame, SharpDX.WIC.PixelFormat.Format32bppPRGBA);

            var newBitmap = SharpDX.Direct2D1.Bitmap1.FromWicBitmap(deviceContext, converter);

            return newBitmap;
        }
    }
}
...