我正在使用D3DImage显示一系列帧,这些帧一个接一个地呈现在同一个Direct3D Surface中。我现在的逻辑是:
这种方法的问题在于,当我们在D3DImage上调用Unlock()时,图像实际上没有被复制,它只被安排在下一个WPF渲染上复制。因此,在WPF有机会显示之前,我们可能会在Direct3D表面上渲染一个新帧。最终结果是我们在显示屏上看到错过的帧。
现在我正在尝试使用单独的Direct3D纹理进行渲染并在显示之前执行复制到“显示纹理”,这样可以获得更好的结果,但会产生大量开销。最好能够知道D3DImage何时完成刷新并立即开始渲染下一帧。这有可能,如果是这样的话怎么样?或者你有一个更好的主意吗?
感谢。
答案 0 :(得分:1)
当WPF要渲染时,会调用CompositionTarget.Rendering event,因此您应该执行Lock()
和Unlock()
。在Unlock()
之后,您可以启动下一个渲染。
您还应该检查RenderingTime
,因为每个帧可能会多次触发事件。尝试这样的事情:
private void HandleWpfCompositionTargetRendering(object sender, EventArgs e)
{
RenderingEventArgs rea = e as RenderingEventArgs;
// It's possible for Rendering to call back twice in the same frame
// so only render when we haven't already rendered in this frame.
if (this.lastRenderTime == rea.RenderingTime)
return;
if (this.renderIsFinished)
{
// Lock();
// SetBackBuffer(...);
// AddDirtyRect(...);
// Unlock();
this.renderIsFinished = false;
// Fire event to start new render
// the event needs to set this.renderIsFinished = true when the render is done
// Remember last render time
this.lastRenderTime = rea.RenderingTime;
}
}
更新以发表评论
你确定有竞争条件吗? This page表示在您致电Unlock()
时会复制后台缓冲区。
如果确实存在竞争条件,那么在渲染代码周围放置锁定/解锁怎么样? This page表示Lock()
会一直阻止,直到副本完成。
答案 1 :(得分:0)
为了与UI并行渲染,看起来干净的方式是渲染到单独的D3D表面,并将它复制到调用之间的显示表面(即传递给SetBackBuffer的表面)锁定()和解锁()。因此算法变为:
Lock()
SetBackBuffer(displaySurface)
AddDirtyRect()
Unlock()
D3DImage explicitely states的文档:
在D3DImage解锁时不要更新Direct3D表面。
这里的痛点是副本,这可能是昂贵的(即如果硬件繁忙则> 2ms)。为了在D3DImage解锁时使用显示器表面(避免在渲染时进行可能代价高昂的操作),人们不得不求助于反汇编和反射来挂钩到D3DImage自己的渲染......