如何在SharpDX中使用ID2D1SpriteBatch?

时间:2019-03-10 23:05:55

标签: c# sharpdx direct2d spritebatch

尝试在direct2d中使用ID2D1SpriteBatch来提高常规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 :(得分:0)

问题是您不能使用带有spritebatch的每个原始抗锯齿。 BeginDraw()之前的这一行对其进行了修复:d2dDeviceContext3.AntialiasMode = AntialiasMode.Aliased;

还终于了解了如何使调试层正常工作。创建设备时包括调试标志(请参见下面的注释)。如果抛出异常,那可能是因为您没有正确版本的Windows sdk。如果您使用的是Visual Studio,请转到Visual Studio安装程序并修改安装,使其包含Windows sdk。

接下来,您需要右键单击您的项目->属性->调试(在左侧面板上)->选中“启用本机代码调试”。完成此操作后,在输出窗口中写了一行,指出:“ D2D DEBUG ERROR-DrawSpriteBatch要求将抗锯齿模式设置为D2D1_ANTIALIAS_MODE_ALIASED。”

我了解到的其他问题与答案没有直接关系,但值得注意:“请注意,SharpDX中的ComObject不会由.NET终结器处理。如果未通过调用Dispose()释放COM对象,则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;
        }
    }
}