C# 如何在处理slimdx时降低cpu影响

C# 如何在处理slimdx时降低cpu影响,c#,directx-9,slimdx,C#,Directx 9,Slimdx,我正在制作一个覆盖其他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 Sy

我正在制作一个覆盖其他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



    }
}
Program.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())
            {

            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Threading.Tasks;
使用System.Windows.Forms;
命名空间覆盖形式
{
静态类程序
{
/// 
///应用程序的主要入口点。
///         
静态void Main()
{
使用(覆盖x=新覆盖())
{
}
}
}
}
请注意:
我已经看到了:


但我使用的是
MessagePump.Run
,不知道如何应用答案。

问题是,即使在不渲染时,我也在使用cpu的全部功能,因此添加

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

问题是,即使在不渲染时,我也在使用cpu的全部功能,因此添加

Thread.Sleep(50)
在dxThread方法结束时,将其降低到
CPU高的原因是SlimDX使用的是
PeekMessage
,而不是大多数Windows应用程序使用的更常见的
GetMessage
。前者与后者不同,前者不等待消息泵中出现消息。换句话说,
GetMessage()
阻止当前线程可能会减少CPU负载,这正是您希望性能良好的Windows桌面应用程序所要做的

MSDN:

从调用线程的消息队列检索消息。该函数将发送传入的消息,直到已发布的消息可供检索为止。 与
GetMessage
不同,
PeekMessage
函数在返回之前不会等待消息被发布

现在,典型的优雅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
overloads for idle,或者按照以下步骤添加睡眠:

更改此项:

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
    }
}
applicationidle上的void(对象发送方,事件参数e)
{
while(AppStillIdle)
{
//在空闲时间渲染帧(没有消息等待)
RenderFrame();

Thread.Sleep(0);//CPU高的原因是SlimDX使用的是
PeekMessage
,而不是大多数Windows应用程序使用的更常见的
GetMessage
。前者不会等待消息出现在消息泵中,而不像后者。换句话说,
GetMessage()
阻止当前线程可能会减少CPU负载,而这正是您希望性能良好的Windows桌面应用程序所做的

MSDN:

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

现在,典型的优雅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
overloads for idle,或者按照以下步骤添加睡眠:

更改此项:

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
    }
}
applicationidle上的void(对象发送方,事件参数e)
{
while(AppStillIdle)
{
//在空闲时间渲染帧(没有消息等待)
RenderFrame();


线程睡眠(0);//这可能是因为您正在使用C#托管代码试图拦截高性能本机游戏创建的帧。如果您注释掉所有绘图,CPU负载会是多少?@MickyD完全相同。@ChuckWalbourn CPU使用率很高,即使不打开游戏链接也可能不相关。这可能是因为您正在使用C#托管代码试图截取高性能本机游戏创建的帧的代码。如果注释掉所有绘图,CPU负载是多少?@MickyD完全相同。@ChuckWalbourn CPU使用率很高,即使不打开游戏链接,该链接也可能不相关。我不需要非常活跃的渲染机制,因为我只使用它来显示来自m的覆盖Y音乐播放器关于当前的歌曲播放、下一首歌曲等……这并不能证明什么。过度睡眠从来不是一个很好的策略来减少CPU负载。当你认为一帧需要16.6毫秒时,你的50毫秒的睡眠时间是每秒60帧渲染3帧的等效时间。我在这页上的建议是在不牺牲帧的情况下减少约20%的CPU负载。我不需要一个非常活跃的渲染机制,因为我只会用它来显示我的音乐播放器中关于当前歌曲播放、下一首歌曲等的覆盖图。这并不能证明什么。过度睡眠从来都不是一个好的策略当你认为一帧需要16.6毫秒时,每秒60帧的3帧的渲染时间是每秒60帧的等效时间。这相当于5%的性能损失。我在这个页面上的建议将在不牺牲帧的情况下减少~(20%)CPU负载。measurably@bigworld12我很怀疑你为什么不你为自己而努力?@bigworld12我很怀疑你为什么不为自己而努力呢?