如何在SharpDX中使用ID2D1SpriteBatch?

问题描述 投票:0回答:1

尝试在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;
        }
    }
}
c# sharpdx direct2d spritebatch
1个回答
0
投票

问题是你不能使用spritebatch进行每个原始抗锯齿。在BeginDraw()修复它之前的这一行:d2dDeviceContext3.AntialiasMode = AntialiasMode.Aliased;

最后还了解了如何使调试层工作。创建设备时包含调试标志(请参阅下面的注释)。如果它抛出一个异常,那可能是因为你没有正确版本的windows sdk。如果您正在使用visual studio,请转到visual studio安装程序并修改您的安装以包含Windows sdk。

接下来,您需要右键单击您的项目 - > properties-> debug(在左侧面板上) - >选中“启用本机代码调试”。在我这样做之后,有一行写入输出窗口,说明:“D2D DEBUG ERROR - DrawSpriteBatch要求将抗锯齿模式设置为D2D1_ANTIALIAS_MODE_ALIASED。”

我学到的其他东西与答案没有直接关系,但值得注意:“请注意,SharpDX中的ComObject不是由.NET终结器处理的。如果没有通过调用Dispose()或ReleaseReference()释放COM对象,它不会释放附加到它的本机对象和内存。“从这里: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;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.