You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D3DImage.Lock触发调度器处理事件致状态异常的技术咨询

问题描述

我开发了一个使用D3dImage类的WPF应用,订阅了CompositionTarget.Rendering事件,通过以下代码更新显示内容:

D3DImageInstance.Lock();
D3DImageInstance.SetBackBuffer(...);
D3DImageInstance.AddDirtyRect(new Int32Rect(0, 0, Width, Height));
D3DImageInstance.Unlock();

调整窗口大小时,Lock()调用期间会触发Resize事件,程序跳转到事件处理函数,调用堆栈如下:

D3DWPFImageSource.Initialize(D3DImageExtManager d3dImageManager, int width, int height, SharpDX.Direct3D11.Texture2D backBufferTexture, SharpDX.Direct3D11.Texture2DDescription textureDesc, int renderSurfaceCount) Line 61    C#
D3DRenderContextWPF.InitResourceBuffers(System.Drawing.Size size) Line 4407 C#
D3DRenderContextWPF.Resize(System.Drawing.Size size) Line 4589  C#
Workspace.OnResize(System.EventArgs e) Line 3875    C#
WorkspaceBase.CreateAndBindTargets() Line 1670  C#
WorkspaceBase.OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo) Line 1845    C#
[External Code] 
>   D3DImageInstance.Lock();
D3DImageExtManager.UpdateBackBufferCommand.Update(SharpDX.Direct3D11.Device device, SharpDX.Direct3D11.DeviceContext context, SharpDX.Direct3D11.Texture2D wpfSharedSurface, System.IntPtr sharedSurfacePtr) Line 222   C#
D3DImageExtManager.CompositionTargetOnRendering(object sender, System.EventArgs eventArgs) Line 156 C#
D3DRenderContextWPF.OnRendering() Line 4491 C#
WorkspaceBase.OnRendering(object sender, System.EventArgs e) Line 1785  C#
[External Code]

在Resize事件处理函数中会重新初始化图形资源,导致回到Rendering处理函数时程序处于不一致状态。想知道:

  1. 这是正常行为吗?
  2. 是否仅由Lock()调用导致?
  3. 该如何正确处理这种情况?
解决方案

1. 行为是否正常?

这是WPF渲染机制下的正常行为。CompositionTarget.Rendering事件在WPF渲染线程触发,窗口Resize事件(SizeChanged)也可能在同一线程被调度执行。调用D3DImage.Lock()时,WPF内部会触发渲染系统的同步操作,间接导致未处理的Resize事件被立即处理,从而出现嵌套调用的情况。

2. 是否仅由Lock()导致?

不完全是,但Lock()是触发嵌套场景的关键节点。D3DImage.Lock()会获取WPF渲染系统的内部锁,这个过程中可能会触发未处理的布局更新或窗口消息(包括Resize)被同步处理。如果没有Lock(),Rendering事件和Resize事件通常按顺序执行,不会出现嵌套。

3. 正确处理方式

  • 用状态标记避免嵌套冲突:
    在Rendering事件处理函数中设置isRendering布尔标记,进入时设为true,退出时设为false。Resize事件处理时先检查该标记,若当前正在渲染,就通过Dispatcher.BeginInvoke将资源初始化逻辑延迟到下一个消息循环执行。示例代码:

    private bool _isRendering;
    
    void CompositionTarget_OnRendering(object sender, EventArgs e)
    {
        if (_isRendering) return;
        _isRendering = true;
        try
        {
            D3DImageInstance.Lock();
            // 执行更新逻辑
            D3DImageInstance.Unlock();
        }
        finally
        {
            _isRendering = false;
        }
    }
    
    void Workspace_OnResize(object sender, EventArgs e)
    {
        if (_isRendering)
        {
            // 延迟处理Resize
            Dispatcher.BeginInvoke(new Action(() => { /* 执行资源重新初始化 */ }), DispatcherPriority.Render);
            return;
        }
        // 正常执行Resize逻辑
        InitResourceBuffers(newSize);
    }
    
  • 分离资源初始化与Rendering逻辑:
    将资源初始化放到独立的后台线程(若允许),或确保Resize时的资源替换是原子性操作。比如维护一个"待激活"的资源集合,在Rendering事件中安全切换到新资源,避免直接修改当前正在使用的资源。

  • 缩短Lock()到Unlock()的执行时间:
    D3DImage.Lock()与Unlock()之间的代码要尽可能简短,减少窗口消息被触发的概率,同时避免调用任何可能触发布局或窗口事件的API。

内容的提问来源于stack exchange,提问作者Simone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 21:39:18