【问题标题】:how to lower cpu impact when dealing with slimdx处理 slimdx 时如何降低 CPU 影响
【发布时间】:2016-07-14 05:19:22
【问题描述】:

我正在制作一个覆盖其他 d3d 游戏的应用程序,
该应用程序运行良好,但它对 cpu 有巨大的性能影响

仅渲染一行时占用 21.4% 的 cpu! 我在 c# 上使用 slimdx 库,这是我的完整代码

OverLay.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SlimDX.Direct3D11;
using SlimDX.Windows;
using System.Runtime.InteropServices;
using System.Security;
using SlimDX;
using SlimDX.DXGI;
using Device = SlimDX.Direct3D9.Device;
using Resource = SlimDX.Direct3D9.Resource;
using System.Threading;
using D3D = SlimDX.Direct3D9;

namespace OverlayForm
{
    public partial class OverLay : RenderForm
    {
        RenderForm form;

        Device device;
        // D3D.Sprite sprite;

        public OverLay()
        {
            InitializeComponent();

            Paint += OverLay_Paint;
            FormBorderStyle = FormBorderStyle.None;
            ShowIcon = false;
            ShowInTaskbar = false;
            TopMost = true;
            WindowState = FormWindowState.Maximized;



            //Make the window's border completely transparant
            //SetWindowLong(Handle , GWL_EXSTYLE , (IntPtr)(GetWindowLong(Handle , GWL_EXSTYLE) ^ WS_EX_LAYERED ^ WS_EX_TRANSPARENT));
            SetWindowLong(Handle , GWL_EXSTYLE , (IntPtr)(GetWindowLong(Handle , GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));

            //Set the Alpha on the Whole Window to 255 (solid)
            SetLayeredWindowAttributes(Handle , 0 , 255 , LWA_ALPHA);



            form = this;
            form.FormClosing += Form_FormClosing;
            //Init DirectX
            //This initializes the DirectX device. It needs to be done once.
            //The alpha channel in the backbuffer is critical.
            D3D.PresentParameters presentParameters = new D3D.PresentParameters();
            presentParameters.Windowed = true;
            presentParameters.SwapEffect = D3D.SwapEffect.Discard;
            presentParameters.BackBufferFormat = D3D.Format.A8R8G8B8;


            device = new Device(new D3D.Direct3D() , 0 , D3D.DeviceType.Hardware , Handle ,
            D3D.CreateFlags.HardwareVertexProcessing , presentParameters);

            //sprite = new D3D.Sprite(device);

            font = new D3D.Font(device , new Font("Arial" , 9 , FontStyle.Regular));
            line = new D3D.Line(this.device);

            MessagePump.Run(form , new MainLoop(dxThread));
        }

        private void Form_FormClosing(object sender , FormClosingEventArgs e)
        {
            device.Dispose();
        }

        int centerx = Screen.PrimaryScreen.WorkingArea.Width / 2;
        int centery = Screen.PrimaryScreen.WorkingArea.Height / 2;
        private void OverLay_Paint(object sender , PaintEventArgs e)
        {
            //Create a margin (the whole form)
            marg.Left = 0;
            marg.Top = 0;
            marg.Right = Width;
            marg.Bottom = Height;

            //Expand the Aero Glass Effect Border to the WHOLE form.
            // since we have already had the border invisible we now
            // have a completely invisible window - apart from the DirectX
            // renders NOT in black.
            DwmExtendFrameIntoClientArea(Handle , ref marg);
        }
        private static D3D.Font font;
        private static D3D.Line line;
        private void dxThread()
        {
            form.TopMost = true;
            device.SetRenderState(D3D.RenderState.ZEnable , false);
            device.SetRenderState(D3D.RenderState.Lighting , false);
            device.SetRenderState(D3D.RenderState.CullMode , D3D.Cull.None);
            device.SetTransform(D3D.TransformState.Projection , Matrix.OrthoOffCenterLH(0 , Width , Height , 0 , 0 , 1));
            device.BeginScene();



            //DrawFilledBox(0 , 0 , 100 , 100 , Color.White);
            //font.DrawString( null, "Swag" , 10, 10 , new Color4(Color.White));            
            //DrawBox(0 , 0 , 10 , 10 , 1 , Color.Green);
            DrawLine(0 , 0 , Screen.PrimaryScreen.Bounds.Width , Screen.PrimaryScreen.Bounds.Height , 2 , Color.Pink);


            device.EndScene();
            device.Present();
        }

        public static void DrawFilledBox(float x , float y , float w , float h , Color Color)
        {
            Vector2[] vLine = new Vector2[2];

            line.GLLines = true;
            line.Antialias = false;
            line.Width = w;

            vLine[0].X = x + w / 2;
            vLine[0].Y = y;
            vLine[1].X = x + w / 2;
            vLine[1].Y = y + h;

            line.Begin();
            line.Draw(vLine , new Color4(Color));
            line.End();
        }
        public static void DrawLine(float x1 , float y1 , float x2 , float y2 , float w , Color Color)
        {
            Vector2[] vLine = new Vector2[2] { new Vector2(x1 , y1) , new Vector2(x2 , y2) };

            line.GLLines = true;
            line.Antialias = false;
            line.Width = w;

            line.Begin();
            line.Draw(vLine , new Color4(Color));
            line.End();

        }
        public static void DrawBox(float x , float y , float w , float h , float px , System.Drawing.Color Color)
        {
            DrawFilledBox(x , y + h , w , px , Color);
            DrawFilledBox(x - px , y , px , h , Color);
            DrawFilledBox(x , y - px , w , px , Color);
            DrawFilledBox(x + w , y , px , h , Color);
        }

        #region Extras
        private Margins marg;
        //this is used to specify the boundaries of the transparent area
        internal struct Margins
        {
            public int Left, Right, Top, Bottom;
        }

        [DllImport("user32.dll" , SetLastError = true)]

        private static extern UInt32 GetWindowLong(IntPtr hWnd , int nIndex);

        [DllImport("user32.dll")]

        static extern int SetWindowLong(IntPtr hWnd , int nIndex , IntPtr dwNewLong);

        [DllImport("user32.dll")]

        static extern bool SetLayeredWindowAttributes(IntPtr hwnd , uint crKey , byte bAlpha , uint dwFlags);

        public const int GWL_EXSTYLE = -20;

        public const int WS_EX_LAYERED = 0x80000;

        public const int WS_EX_TRANSPARENT = 0x20;

        public const int LWA_ALPHA = 0x2;

        public const int LWA_COLORKEY = 0x1;

        [DllImport("dwmapi.dll")]
        static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd , ref Margins pMargins);

        #endregion



    }
}

程序.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OverlayForm
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>        
        static void Main()
        {
            using (OverLay x = new OverLay())
            {

            }
        }
    }
}

请注意:
我已经看到了:Very high CPU usage directx 9

但我正在使用MessagePump.Run 并且不知道如何应用答案。

【问题讨论】:

  • 这可能是因为您正在使用 C# 托管代码尝试截取高性能原生游戏创建的帧。
  • 如果您注释掉 all 您的绘图,CPU 负载是多少?
  • @MickyD 完全相同..
  • @ChuckWalbourn cpu 使用率很高,即使没有打开游戏
  • 那个链接可能不相关。

标签: c# directx-9 slimdx


【解决方案1】:

高 CPU 的原因是 SlimDX 使用的是PeekMessage,而不是更常见的GetMessage(大多数 Windows 应用程序都使用)。与后者不同,前者不等待消息出现在消息泵中。换句话说,GetMessage()阻塞当前线程可能会降低 CPU 负载,而这正是您希望表现良好的 Windows 桌面应用程序执行的操作。

MSDN:

从调用线程的消息队列中检索消息。该函数分派传入的已发送消息,直到发布的消息可用于检索。 与GetMessage 不同,PeekMessage 函数在返回More... 之前不会等待消息发布

现在典型的优雅 Windows 消息泵如下所示:

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
} 

...然而 SlimDX 使用了一些人所说的动作游戏循环:

static bool AppStillIdle
{
    get
    {
        Message msg;
        return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
    }
}

public void MainLoop()
{
    // hook the application's idle event
    Application.Idle += new EventHandler(OnApplicationIdle);
    Application.Run(form);
}

void OnApplicationIdle(object sender, EventArgs e)
{
    while (AppStillIdle)
    {
        // Render a frame during idle time (no messages are waiting)
        RenderFrame();
    }
}

没有任何东西可画,您将体验到一个非常紧凑的PeekMessage 循环,中间无需等待!

我的建议是,您要么使用 MessagePump.Run 重载之一进行空闲,要么按以下方式添加睡眠:

改变这个:

void OnApplicationIdle(object sender, EventArgs e)
{
    while (AppStillIdle)
    {
        // Render a frame during idle time (no messages are waiting)
        RenderFrame();
    }
}

...到这个:

void OnApplicationIdle(object sender, EventArgs e)
{
    while (AppStillIdle)
    {
        // Render a frame during idle time (no messages are waiting)
        RenderFrame();

        Thread.Sleep(0); // <------------- be graceful
    }
}

注意Thread.Sleep(0) 的使用。这会暂停最少的时间,同时仍允许将线程交给操作系统。

MSDN:

线程暂停的毫秒数。如果 millisecondsTimeout 参数的值是,则线程将其时间片的剩余部分让给任何准备运行的具有相同优先级的线程。如果没有其他同等优先级的线程可以运行,则不会暂停当前线程的执行。

我在你的回答中看到你已经有一个Thread.Sleep(50),但现在很高兴知道为什么 SlimDX 首先需要一个Sleep,而50 的值可能太高了。

获取消息

操作:

我不需要一个非常活跃的渲染机制,因为我只会使用它来显示我的音乐播放器关于当前歌曲播放、下一首歌曲等的叠加层

考虑到这种情况,最节省 CPU 的方法是使用GetMessage() 而不是PeekMessage(),将动作循环替换为回合制游戏循环。然后将您的渲染放入应用程序的OnIdle() 回调中。

作为Nick Dandoulakis saysWindows Game Loop 50% CPU on Dual Core

尼克:

这是动作游戏的标准游戏循环,您必须在其中更新对象位置/游戏世界。 如果您正在制作棋盘游戏GetMessage 会是更好的选择。 这真的取决于你正在制作什么游戏。 More...

不需要Sleep()

【讨论】:

  • @bigworld12 我非常怀疑
  • 你为什么不自己尝试一下?
【解决方案2】:

问题是我在不渲染时也使用了 CPU 的全部功能,所以添加

Thread.Sleep(50);
在 dxThread 方法结束时将其降低到仅

【讨论】:

  • 我不需要一个非常活跃的渲染机制,因为我只会使用它来显示我的音乐播放器关于当前歌曲播放、下一首歌曲等的叠加......
  • 这并不能证明什么。过度睡眠从来都不是减少 CPU 负载的好策略。当您考虑一帧需要 16.6 毫秒时,您 50 毫秒的睡眠时间相当于以每秒 60 帧的速度渲染 3 帧的时间。这相当于性能损失 5%。我在此页面上的建议将在不显着牺牲帧的情况下减少约 20 % 的 CPU 负载
猜你喜欢
  • 1970-01-01
  • 2016-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 2016-06-11
  • 1970-01-01
  • 2015-11-12
相关资源
最近更新 更多