Windows 表单上的 SharpDX - 最小化屏幕时的竞争条件
SharpDX on Windows form - Race condition when minimizing screen
我有一个问题,一个星期以来我都无法解决,我希望有人能经历过这个。
我将 SharpDX 与 windows 表单项目一起使用,基本上有一个带有图片框和一些面板的表单。
当我缩放或平移图像时,典型的 Swapchain.Present(1,PresentFlags.None) 效果很好。但是,我遇到了这种奇怪的情况,当我最小化屏幕并重新打开它时,所有交换链内容都被隐藏起来,就像它已被删除一样。但是,我知道它没有,这可能是 GDI 刷新和 SharpDX 之间的竞争条件。
因此,我尝试在每个控件中覆盖 WndProc(Message m) 并处理它的绘制和擦除背景事件。这是行不通的。当我调试时,windows 消息总是不时地不同,所以很难找出可能出了什么问题。
如果有人在 Windows 形式上混合 SharpDX(或 directX)时遇到过这种行为,我真的很想知道答案。
这是我处理 SharpDX DeviceContext 的调整大小事件的方式
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) where m = WM_PAINT.
时它正在发生
我不能忽略绘制消息,因为如果我这样做,我的控件仍然无效,它会向事件队列添加一个新的 WM_PAINT 并使我进入无限循环。我真的不能做 base.Wndproc(m) 因为这是我的交换链隐藏的地方。
他们有没有办法处理(忽略)这个事件消息而不让它回到循环中,因为 SharpDX 不需要这个函数,因为它是控件上的一个绘制层。
当您只想调整交换链的大小时,您不必重新创建 DeviceContext。也许这是您问题的第一个原因,因为交换链是使用与后台缓冲区不同的 DeviceContext 创建的。
对我来说,这在 C# 中有效:
首先将事件处理程序添加到控件的调整大小事件。无需覆盖 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;
有了这个我可以保存控件的旧大小以供比较。我只是在调整大小完成后调整交换链的大小,而不是针对每个事件(比如调整 window 大小时)。另外,如果控件未最小化,我只会调整大小:
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 是我的自定义class):
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);
}
}
我有一个问题,一个星期以来我都无法解决,我希望有人能经历过这个。
我将 SharpDX 与 windows 表单项目一起使用,基本上有一个带有图片框和一些面板的表单。
当我缩放或平移图像时,典型的 Swapchain.Present(1,PresentFlags.None) 效果很好。但是,我遇到了这种奇怪的情况,当我最小化屏幕并重新打开它时,所有交换链内容都被隐藏起来,就像它已被删除一样。但是,我知道它没有,这可能是 GDI 刷新和 SharpDX 之间的竞争条件。
因此,我尝试在每个控件中覆盖 WndProc(Message m) 并处理它的绘制和擦除背景事件。这是行不通的。当我调试时,windows 消息总是不时地不同,所以很难找出可能出了什么问题。
如果有人在 Windows 形式上混合 SharpDX(或 directX)时遇到过这种行为,我真的很想知道答案。
这是我处理 SharpDX DeviceContext 的调整大小事件的方式
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) where m = WM_PAINT.
时它正在发生我不能忽略绘制消息,因为如果我这样做,我的控件仍然无效,它会向事件队列添加一个新的 WM_PAINT 并使我进入无限循环。我真的不能做 base.Wndproc(m) 因为这是我的交换链隐藏的地方。
他们有没有办法处理(忽略)这个事件消息而不让它回到循环中,因为 SharpDX 不需要这个函数,因为它是控件上的一个绘制层。
当您只想调整交换链的大小时,您不必重新创建 DeviceContext。也许这是您问题的第一个原因,因为交换链是使用与后台缓冲区不同的 DeviceContext 创建的。 对我来说,这在 C# 中有效:
首先将事件处理程序添加到控件的调整大小事件。无需覆盖 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;
有了这个我可以保存控件的旧大小以供比较。我只是在调整大小完成后调整交换链的大小,而不是针对每个事件(比如调整 window 大小时)。另外,如果控件未最小化,我只会调整大小:
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 是我的自定义class):
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);
}
}