Windows窗体上的SharpDX - 最小化屏幕时的竞争条件

时间:2017-08-16 13:07:59

标签: c# winforms directx sharpdx

我有一个问题,因为一周后我无法修复,我希望有人可能曾经历过这个。

我正在使用带有Windows窗体项目的SharpDX,基本上有一个带有图片框的表单和一些面板。

当我缩放或翻译图像时,典型的Swapchain.Present(1,PresentFlags.None)效果很好。然而,我遇到这种奇怪的情况,当我最小化我的屏幕并重新打开它时,所有交换链内容都被隐藏,就像它已被删除一样。但是,我知道它没有,这可能是GDI刷新和SharpDX之间的竞争条件。

所以,我试图在每个控件中覆盖WndProc(Message m)并处理它的paint和eraseBackground事件。这不起作用。当我调试时,Windows消息总是不同的,所以很难弄清楚可能出错的地方。

如果有人在Windows窗体上混合SharpDX(或directX)时遇到过这种行为,我真的很想回答。

以下是我处理SharpDX DeviceContext的resize事件的方法

Public Overrides Sub Resize(Width As Integer, Height As Integer)
      If m_SwapChain IsNot Nothing Then
         If m_BackBuffer IsNot Nothing Then
            m_BackBuffer.Dispose()
         End If
         If m_2DDeviceContext IsNot Nothing Then
            m_2DDeviceContext.Dispose()
         End If
         If m_2DTarget IsNot Nothing Then
            m_2DTarget.Dispose()
         End If

         m_SwapChain.ResizeBuffers(2, Width, Height, Format.B8G8R8A8_UNorm, SwapChainFlags.GdiCompatible)

         m_BackBuffer = m_SwapChain.GetBackBuffer(Of Surface)(0)

         m_2DDeviceContext = New SharpDX.Direct2D1.DeviceContext(m_2DDevice, SharpDX.Direct2D1.DeviceContextOptions.EnableMultithreadedOptimizations)

         m_2DTarget = New SharpDX.Direct2D1.Bitmap(m_2DDeviceContext, m_BackBuffer, m_Properties)
         m_2DDeviceContext.AntialiasMode = AntialiasMode.PerPrimitive

         m_2DDeviceContext.Target = m_2DTarget

         CType(m_Context, GPUDrawingContext).DeviceContext = m_2DDeviceContext
      End If

   End Sub

编辑: 在尝试了很多东西之后,我意识到当我调用base.Wndproc(m)时,它正好发生了m = WM_PAINT。

我不能忽略paint消息,因为如果我这样做,我的控件仍然无效,它将向事件队列添加一个新的WM_PAINT并将我发送到无限循环。我真的不能做base.Wndproc(m)因为这是我的交换链被隐藏的地方。

他们是否可以处理(忽略)此事件消息而不将其返回循环,因为SharpDX不需要此功能,因为它是控件上的绘制层。

1 个答案:

答案 0 :(得分:1)

当您只想调整交换链的大小时,您不必重新创建DeviceContext。也许这是问题的第一个原因,因为交换链是使用与后备缓冲区不同的DeviceContext创建的。 对我来说,这是在C#中工作的:

首先将eventhandlers添加到控件的resize事件中。无需覆盖WM_PAINT:

form.ResizeBegin += (o, e) => {
    formHeight = ((Form)o).Height;
    formWidth = ((Form)o).Width;
};
form.ResizeBegin += (o, e) => {
    isResizing = true;
};
form.ResizeEnd += (o, e) => {
    isResizing = false;
    HandleResize(o, e);
};
form.SizeChanged += HandleResize;

用这个我可以保存控件的旧尺寸进行比较。我只是在调整大小完成后调整交换链的大小而不是每个事件(例如调整窗口大小时)。如果控件未最小化,我也只会调整大小:

private void HandleResize(object sender, System.EventArgs e) {
    Form f = (Form)sender;
    if ((f.ClientSize.Width != formWidth || f.ClientSize.Height != formHeight)
        && !isResizing
        && !(f.WindowState == FormWindowState.Minimized)) {
        formWidth = f.ClientSize.Width;
        formHeight = f.ClientSize.Height;

        DoResize(formWidth, formHeight);
    }
}

最后DoResize正在关注(renderTarget是我的自定义类):

private void DoResize(int width, int height) {
    renderTarget.Dispose();
    swapChain.ResizeBuffers(1, width, height, Format.R8G8B8A8_UNorm, SwapChainFlags.AllowModeSwitch);
    using (var resource = Resource.FromSwapChain<Texture2D>(swapChain, 0)) {
        //recreate the rendertarget with the new backbuffer
        renderTarget.Resize(width, height, resource);
    }
}