防止 Direct3D 视口图像缩放 (SlimDX)
我在窗口中的 SlimDX 中设置了 Direct3D11 场景。渲染是在单独的线程中完成的。
有没有办法阻止渲染器在绘制到调整大小的控件时拉伸图像?我尝试过 ModeDescription.Scaling = DisplayModeScaling.Centered
但似乎没有任何效果。我有什么遗漏的吗?
(我已经在更新渲染目标大小。我问这个问题的原因是,当我调整控件大小时,它会拉伸图像以在渲染目标更新为新大小之前瞬间填充控件。结果是当我调整它的大小时,它会闪烁得很厉害,如果我可以稍微快一点地重置渲染目标,那么将图像保持在角落而不缩放就完全没问题,因为最终它根本不会缩放。 )
解决方法1:可以将渲染目标放在控件内。当窗口调整大小时,仅通过一种特殊方法调整控件大小,该方法首先停止渲染,然后更新缓冲区并再次开始渲染。不过,这有点黑客行为。我必须等待渲染周期完成,然后阻止它,然后调整大小,然后取消阻止。
解决方法 2: 类似的解决方法是检查渲染循环中的调整大小标志,而不是中断它。渲染器应该能够直接绘制而无需缩放。从性能角度来看,这是更容易接受的。但是,必须对 UI 线程进行阻塞调用才能执行实际的大小调整。
解决方法 3:无需调整控件的大小,而是可以将其设置为最大尺寸,但进行裁剪(在窗口内)。无需调整大小,但必须以与上述解决方法类似的方式维护剪刀矩形,除非您不介意渲染大量屏幕外像素。渲染二十个左右的额外行和列的像素确实具有在窗口大小调整回较大时在边缘提供即时图像的有利效果。
理想调整大小应该直接从 UI 线程完成(不要浪费时间延迟它并从渲染线程重新进入 UI 线程)。这不是渲染线程的责任,它会减慢渲染线程的速度。同样,缓冲区大小调整应该在渲染线程中完成,以获得最大性能(不要浪费时间等待/阻塞/调整大小/解除阻塞)。这不起作用的唯一原因是,如果在调整缓冲区大小之前完成调整大小,则渲染线程会缩放。
所以我的问题仍然存在:有没有办法在不缩放的情况下进行渲染?
I have a Direct3D11 scene set up in SlimDX in a window. The rendering is done in a separate thread.
Is there a way to keep the renderer from stretching the image when it draws to the resized control? I've tried ModeDescription.Scaling = DisplayModeScaling.Centered
and it doesn't seem to have any effect. Is there something I'm missing?
(I already am updating the render target size. The reason I ask this is that when I resize the control it stretches the image to fill the control for a split second before the render target gets updated with the new size. This has the result that as I resize it, it flickers terribly. If I could reset the render target just slightly faster it might get rid the the flicker. Keeping the image in the corner without scaling it is perfectly fine since ultimately it won't be scaling at all.)
Workaround 1: One can put the render target inside a control. When the window resizes only resize the control though a special method that first stops the rendering, then update the buffers and begins rendering again. It's a bit of hack, though. I have to wait for the render cycle to complete, then block it, then resize, then unblock.
Workaround 2: A similar workaround is to check for a resize flag in the render loop rather than interrupting it. The renderer should be able to draw directly without scaling. This is much more acceptable performancewise. However, a blocking call to the UI thread must be made to execute the actual resize.
Workaround 3: Rather than resizing the control at all, one could make it as large as the maximum size it could be, but clipped (inside the window). No resize is necessary, but a scissor rectangle must be maintained in a similar manner to the workarounds above unless you don't mind rendering a whole lot of offscreen pixels. Rendering twenty or so extra rows and columns of pixels does have the favorable effect of supplying immediate image at the edge when the window is resized back larger.
Ideally the resize should be done directly from the UI thread (no fooling around with delaying it and reentering the UI thread from the render thread). It's not the render thread's responsibility and it slows it down. Likewise, the buffer resize should be done in the render thread for maximum performance (no fooling around with waiting/blocking/resizing/unblocking). The only reason this does not work is that the render thread scales if the resize is done before the buffers are resized.
So my question still stands: Is there a way to render without scaling?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我将根据所涉及的原始 Win32 API 来回答这个问题,这可能需要一些技巧才能转换为托管 .NET 环境和环境。苗条DX。
当您拖动窗口或调整窗口大小时,Windows 基本上会劫持您的消息泵并创建一个专门设计用于有效执行调整大小逻辑的新消息泵。此操作在完成之前或多或少是模态的。您通常收到的许多消息实际上都会被阻止,直到调整大小或拖动完成为止。在应用程序条款中,当此行为开始时,您会收到 WM_ENTERSIZEMOVE,当其结束时,您会收到 WM_EXITSIZEMOVE 或 WM_CAPTURECHANGED。您需要检查这两条消息,因为在拖动时按 alt-tab 键将发送 WM_CAPTURECHANGED 而不会发送 WM_EXITSIZEMOVE!
所有这一切意味着,当您收到 WM_ENTERSIZEMOVE 时,您可以设置一个标志并知道之后发生的所有 WM_SIZE 和 WM_MOVE 消息都是针对拖动操作的。通常,调整渲染目标的大小以及跟踪和取消分配所有默认池资源是一项非常缓慢的操作,值得推迟到拖动/调整大小完成为止。这具有使窗口拉伸的副作用,这与您在此处描述的问题完全相同,并且您想要解决该问题。
应该可以在 WM_SIZE、WM_MOVE 或 WM_SIZING 和 WM_MOVING 中添加特殊处理程序,在它们发生时强制通过 SendMessage(而不是 PostMessage)进行同步渲染和重绘,但您需要确保仅在内部时执行此操作WM_ENTERSIZEMOVE 拥有的模态循环。
I am going to answer this in terms of the raw Win32 APIs involved, which might require a bit of finesse to translate to a managed .NET environment & SlimDX.
When you are dragging or resizing a window, windows basically hijacks your message pump and creates a new one specifically designed to do the resizing logic efficiently. This operation is more or less modal until it is completed. Many messages you would normally get are quite literally blocked until the resize or drag is completed. In the app terms you get a WM_ENTERSIZEMOVE when this behavior begins, and either WM_EXITSIZEMOVE or WM_CAPTURECHANGED when it ends. You need to check both of these messages, as alt-tabing out when doing a drag will send WM_CAPTURECHANGED and never a WM_EXITSIZEMOVE!
All this means that when you get a WM_ENTERSIZEMOVE you can set a flag and know that all WM_SIZE and WM_MOVE messages that occur afterwards are for the drag operation. Typically resizing rendertargets and tracking down and de-allocating all default pool resources is a very slow operation, and well worth defering until the drag/resize has completed. This has the side effect of making the window stretch which is exactly the same problem you are describing here, and you want to fix that.
It should be possible to add special handlers in WM_SIZE, WM_MOVE, or WM_SIZING, and WM_MOVING that forces a syncronous render and repaint via SendMessage (as opposed to PostMessage) when they occur, but you will need to make sure you only do this when inside the modal loop owned by WM_ENTERSIZEMOVE.
我建议要么强制窗口为固定大小(这似乎很常见),要么在更改完成之前不重绘 D3D 控件(或者简单地将其清除为黑色)。
似乎有一种方法可以做你想做的事,因为我见过能够改变大小而不闪烁的游戏,但不幸的是,我不知道这是如何做到的。锁定窗口大小并提供菜单是旧的解决方案。
I would recommend either forcing the window to a fixed size (which seems rather common) or not redrawing the D3D control until the changes are done (that or simply clear it black).
There seems to be a way to do what you want, as I have seen games capable of changing size with no flicker, but I've no idea how that's done, unfortunately. Locking the window size and providing a menu was the old solution.