前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WPF 模拟 WPFMediaKit 的 D3D 配置用来测试4k性能

WPF 模拟 WPFMediaKit 的 D3D 配置用来测试4k性能

作者头像
林德熙
发布2021-12-23 10:51:06
8480
发布2021-12-23 10:51:06
举报
文章被收录于专栏:林德熙的博客林德熙的博客

本文告诉大家我在测试 WPFMediaKit 的 D3D 配置性能影响在 4k 分辨率设备下采用高清摄像头的性能

测试效果是 10 代 i3 带 4G 内存和集显 UHD 630 在 4k 下,跑满 36 Hz 不卡。以下是具体测试的逻辑

在 WPFMediaKit 定义渲染在 Vmr9Allocator 类里面,在 CreateDevice 方法上采用如下代码进行初始化 IDirect3DDevice9 设备

代码语言:javascript
复制
        private void CreateDevice()
        {
            if (m_device != null)
                return;

            var param = new D3DPRESENT_PARAMETERS
            {
                Windowed = 1,
                Flags = ((short)D3DPRESENTFLAG.D3DPRESENTFLAG_VIDEO),
                BackBufferFormat = D3DFORMAT.D3DFMT_X8R8G8B8,
                SwapEffect = D3DSWAPEFFECT.D3DSWAPEFFECT_COPY
            };

            /* The COM pointer to our D3D Device */
            IntPtr dev;

            /* Windows Vista runs much more performant with the IDirect3DDevice9Ex */
            if (IsVistaOrBetter)
            {
                m_d3dEx.CreateDeviceEx(0, D3DDEVTYPE.D3DDEVTYPE_HAL, m_hWnd,
                  CreateFlags.D3DCREATE_HARDWARE_VERTEXPROCESSING | CreateFlags.D3DCREATE_MULTITHREADED,
                  ref param, IntPtr.Zero, out dev);
            }
            else/* Windows XP */
            {
                m_d3d.CreateDevice(0, D3DDEVTYPE.D3DDEVTYPE_HAL, m_hWnd,
                  CreateFlags.D3DCREATE_SOFTWARE_VERTEXPROCESSING | CreateFlags.D3DCREATE_MULTITHREADED,
                  ref param, out dev);
            }

            m_device = (IDirect3DDevice9)Marshal.GetObjectForIUnknown(dev);
            Marshal.Release(dev);
        }

在 InitializeDevice 使用如下代码初始化

代码语言:javascript
复制
                            hr = m_device.CreateTexture(lpAllocInfo.dwWidth, 
                                                        lpAllocInfo.dwHeight, 
                                                        1, 
                                                        1,
                                                        D3DFORMAT.D3DFMT_X8R8G8B8, 
                                                        0, 
                                                        out m_privateTexture, 
                                                        IntPtr.Zero);

                            DsError.ThrowExceptionForHR(hr);

                            hr = m_privateTexture.GetSurfaceLevel(0, out m_privateSurface);
                            DsError.ThrowExceptionForHR(hr);

通过如上代码创建的 IDirect3DSurface9 类型的 m_privateSurface 可以作为 D3DImage 的使用参数

为了测试此方式的参数创建的 IDirect3DTexture9 在 WPF 里的性能,本文将扔掉摄像头部分,换 D2D 渲染,测试在 4k 的性能。因为加上摄像头还有解码部分的逻辑,这部分逻辑将让说明性能失败

创建一个空 WPF 应用,在 MainWindow_Loaded 添加初始化代码

使用 Direct3DCreate9Ex 函数创建 IDirect3D9Ex 对象

代码语言:javascript
复制
            var hr = Direct3DCreate9Ex(D3D_SDK_VERSION, out var direct3D9Ex);

所使用的参数如下

代码语言:javascript
复制
        [ComImport, SuppressUnmanagedCodeSecurity,
        Guid("81BDCBCA-64D4-426d-AE8D-AD0147F4275C"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown), SuppressUnmanagedCodeSecurity]
        public interface IDirect3D9
        {
        	// 忽略代码
        }

        [ComImport, SuppressUnmanagedCodeSecurity,
        Guid("02177241-69FC-400C-8FF1-93A44DF6861D"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown), SuppressUnmanagedCodeSecurity]
        public interface IDirect3D9Ex : IDirect3D9
        {
        	// 忽略代码
        }

        /// <summary>
        /// The SDK version of D3D we are using
        /// </summary>
        private const ushort D3D_SDK_VERSION = 32;

        [DllImport("d3d9.dll", EntryPoint = "Direct3DCreate9Ex", CallingConvention = CallingConvention.StdCall),
        SuppressUnmanagedCodeSecurity]
        public static extern int Direct3DCreate9Ex(ushort SDKVersion, [Out] out IDirect3D9Ex ex);

拷贝 Vmr9Allocator 的代码初始化,但是需要修改部分逻辑,如删掉 BackBufferFormat 的设置和更改 SwapEffect 的参数,加上 hDeviceWindow 和 PresentationInterval 定义

代码语言:javascript
复制
            m_hWnd = GetDesktopWindow();

            var param = new D3DPRESENT_PARAMETERS
            {
                Windowed = 1,
                Flags = ((short) D3DPRESENTFLAG.D3DPRESENTFLAG_VIDEO),
                /*
                D3DFMT_R8G8B8:表示一个24位像素,从左开始,8位分配给红色,8位分配给绿色,8位分配给蓝色。

                D3DFMT_X8R8G8B8:表示一个32位像素,从左开始,8位不用,8位分配给红色,8位分配给绿色,8位分配给蓝色。

                D3DFMT_A8R8G8B8:表示一个32位像素,从左开始,8位为ALPHA通道,8位分配给红色,8位分配给绿色,8位分配给蓝色。

                D3DFMT_A16B16G16R16F:表示一个64位浮点像素,从左开始,16位为ALPHA通道,16位分配给蓝色,16位分配给绿色,16位分配给红色。

                D3DFMT_A32B32G32R32F:表示一个128位浮点像素,从左开始,32位为ALPHA通道,32位分配给蓝色,32位分配给绿色,32位分配给红色。
                 */
                //BackBufferFormat = D3DFORMAT.D3DFMT_X8R8G8B8,

                //SwapEffect = D3DSWAPEFFECT.D3DSWAPEFFECT_COPY
                SwapEffect = D3DSWAPEFFECT.D3DSWAPEFFECT_DISCARD,

                hDeviceWindow = GetDesktopWindow(), // 添加
                PresentationInterval = (int) D3D9.PresentInterval.Default,
            };

和 Vmr9Allocator 使用相同代码创建设备

代码语言:javascript
复制
            /* The COM pointer to our D3D Device */
            IntPtr dev;
            m_d3dEx.CreateDeviceEx(0, D3DDEVTYPE.D3DDEVTYPE_HAL, m_hWnd,
                Direct3D.CreateFlags.D3DCREATE_HARDWARE_VERTEXPROCESSING | Direct3D.CreateFlags.D3DCREATE_MULTITHREADED
                          | Direct3D.CreateFlags.D3DCREATE_FPU_PRESERVE,
                ref param, IntPtr.Zero, out dev);

            m_device = (IDirect3DDevice9) Marshal.GetObjectForIUnknown(dev);
            // 只是减少引用计数而已,现在换成 m_device 了
            Marshal.Release(dev);

为了在 D3D9 里使用上 D2D 需要创建 D3D11 设备,这部分逻辑只是用来测试,为了方便代码,我加上 SharpDx 的引用。值得一说的是 SharpDx 当前官方不维护了,可以选择的代替请看 SharpDx 的代替项目

代码语言:javascript
复制
  <ItemGroup>
    <PackageReference Include="SharpDX" Version="4.2.0" />
    <PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />
    <PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
    <PackageReference Include="SharpDX.Direct3D9" Version="4.2.0" />
  </ItemGroup>

参考 WPF 使用 SharpDX 在 D3DImage 显示 的定义逻辑,在 CreateRenderTarget 方法加上代码

代码语言:javascript
复制
        private Texture2D CreateRenderTarget()
        {
            var width = ImageWidth;
            var height = ImageHeight;

            var renderDesc = new D3D11.Texture2DDescription
            {
                BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource,
                Format = DXGI.Format.B8G8R8A8_UNorm,
                Width = width,
                Height = height,
                MipLevels = 1,
                SampleDescription = new DXGI.SampleDescription(1, 0),
                Usage = D3D11.ResourceUsage.Default,
                OptionFlags = D3D11.ResourceOptionFlags.Shared,
                CpuAccessFlags = D3D11.CpuAccessFlags.None,
                ArraySize = 1
            };

            var device = new D3D11.Device(DriverType.Hardware, D3D11.DeviceCreationFlags.BgraSupport);
            var renderTarget = new D3D11.Texture2D(device, renderDesc);
            var surface = renderTarget.QueryInterface<DXGI.Surface>();

            var d2DFactory = new D2D.Factory();

            var supportRequired = FormatSupport.RenderTarget;
            var isSupported = device.CheckFormatSupport(DXGI.Format.B8G8R8A8_UNorm).HasFlag(supportRequired);
            if (isSupported)
            {

            }

            var renderTargetProperties =
                new D2D.RenderTargetProperties(new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied));
            _d2DRenderTarget = new D2D.RenderTarget(d2DFactory, surface, renderTargetProperties);

            device.ImmediateContext.Rasterizer.SetViewport(0, 0, ImageWidth, ImageHeight);

            return renderTarget;
        }

调用 CreateRenderTarget 可以拿到 D3D11.Texture2D 对象,可以使用此作为 SharedHandle 创建 IDirect3DTexture9 对象

代码语言:javascript
复制
            D3D11.Texture2D d3d11Texture2D = CreateRenderTarget();

            var format = TranslateFormat(TranslateFormat(d3d11Texture2D));

            var dxgiResource = d3d11Texture2D.QueryInterface<DXGI.Resource>();
            var pSharedHandle = dxgiResource.SharedHandle;

            hr = m_device.CreateTexture(ImageWidth,
                ImageHeight,
                1,
                1,
                format,
                0,
                out m_privateTexture,
                ref pSharedHandle);

上面代码的 TranslateFormat 如下

代码语言:javascript
复制
        private static D3DFORMAT TranslateFormat(D3D9.Format format)
         => format switch
         {
             D3D9.Format.A8R8G8B8 => D3DFORMAT.D3DFMT_A8R8G8B8,
             _ => throw new ArgumentException(),
         };

        private static D3D9.Format TranslateFormat(D3D11.Texture2D texture)
        {
            switch (texture.Description.Format)
            {
                case SharpDX.DXGI.Format.R10G10B10A2_UNorm:
                    return D3D9.Format.A2B10G10R10;
                case SharpDX.DXGI.Format.R16G16B16A16_Float:
                    return D3D9.Format.A16B16G16R16F;
                case SharpDX.DXGI.Format.B8G8R8A8_UNorm:
                    return D3D9.Format.A8R8G8B8;
                default:
                    return D3D9.Format.Unknown;
            }
        }

拿到 m_privateTexture 对象,即可使用 GetSurfaceLevel 方法获取到可以给 D3DImage 的 BackBuffer 的参数

代码语言:javascript
复制
            hr = m_privateTexture.GetSurfaceLevel(0, out m_privateSurface);

            var backBuffer = Marshal.GetIUnknownForObject(m_privateSurface);

            D3DImage.Lock();
            D3DImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, backBuffer, true);
            D3DImage.Unlock();

以上就完成了初始化逻辑,参数和 WPFMediaKit 相同,接下来是通过 D2D 进行渲染

代码语言:javascript
复制
        private async void Render()
        {
            float x = 0;
            float y = 0;
            const float dx = 1;
            const float dy = 1;

            while (Dispatcher.CheckAccess())
            {
                var renderTarget = _d2DRenderTarget;

                renderTarget.BeginDraw();

                renderTarget.Clear(new RawColor4(Random.Shared.NextSingle(), Random.Shared.NextSingle(), Random.Shared.NextSingle(), 1));
                var brush = new D2D.SolidColorBrush(_d2DRenderTarget, new RawColor4(1, 0, 0, 1));

                renderTarget.DrawRectangle(new RawRectangleF(x, y, x + 10, y + 10), brush);

                x += dx;
                y += dy;
                if (x >= ImageWidth)
                {
                    x = 0;
                }

                if (y >= ImageHeight)
                {
                    y = 0;
                }

                renderTarget.EndDraw();

                D3DImage.Lock();
                D3DImage.AddDirtyRect(new Int32Rect(0, 0, D3DImage.PixelWidth, D3DImage.PixelHeight));
                D3DImage.Unlock();

                Image.InvalidateVisual();

                await Task.Delay(16);
            }
        }

以上就是测试 WPFMediaKit 的代码

本文所有代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

代码语言:javascript
复制
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 01e1dadff23f9481fa5ab99a52f2c0b64ad96fac

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

代码语言:javascript
复制
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 NaferfairqeLidajekawnal 文件夹

测试效果如下:

测试机器配置如下

  • CPU i3 10100
  • 内存 4G 2667MHz
  • GPU Intel(R) UHD Graphics 630

系统版本是 19041.1348 版本

运行效果为最大化窗口 4k 分辨率,刷新率 36 左右下跑满 GPU 但不卡。其中 GPU 有百分之20是 DWM 占用

更多 DX 相关请看 WPF 使用 SharpDx 渲染博客导航

Direct3D 9Ex improvements - Win32 apps

Surface sharing between Windows graphics APIs - Win32 apps

IDirect3DSurface9 (d3d9helper.h) - Win32 apps

IDirect3DSurface9::LockRect (d3d9helper.h) - Win32 apps

IDirect3DSurface9::GetDC (d3d9helper.h) - Win32 apps

SuppressUnmanagedCodeSecurityAttribute 类 (System.Security)

c# - SharpDX. Unsupported pixel format - Stack Overflow

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档