本文记录一个 WPF 已知问题,此问题已经被我修复。传入错误的数据给到 WriteableBitmap 对象,比如调用 WritePixels 时传入错误的 stride 数值,将可能导致渲染线程进入无限自旋锁

问题描述

应用程序停止渲染,或者是界面未响应。表现是在 渲染 线程卡住,从任务管理器看可以看到有一个 CPU 核在跑,但没跑满。进行本机代码调试可以看到卡在如下调用堆栈

 	wpfgfx_cor3.dll!CWGXBitmapLockState::LockRead() line 1086	C++
wpfgfx_cor3.dll!CWGXBitmap::HrLock(const tagRECT & rcLock={...}, MilPixelFormat::Enum pxlFormat=BGRA32bpp, unsigned int cbStride=1200, unsigned int cbBufferSize=1200, void * pvPixels=0x0000028eea8a0000, unsigned long dwFlags=1, IWGXBitmapLock * * ppILock=0x0000009a90b7b268, int) line 743 C++
wpfgfx_cor3.dll!CSystemMemoryBitmap::Lock(const WICRect * prcLock, unsigned long dwFlags=1, IWGXBitmapLock * * ppILock=0x0000009a90b7b268) line 176 C++
wpfgfx_cor3.dll!CWriteProtectedBitmap::Lock(const WICRect * prcLock, unsigned long dwFlags, IWGXBitmapLock * * ppILock) line 125 C++
wpfgfx_cor3.dll!CWGXBitmap::CopyPixels(const WICRect * prc=0x0000009a90b7b4a0, unsigned int cbStride=1200, unsigned int cbBufferSize=1200, unsigned char * pbPixels=0x0000028edfc56540) line 477 C++
wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028edfc56540) line 411 C++
WindowsCodecs.dll!00007ffcaa688759()
WindowsCodecs.dll!00007ffcaa6ab5d3()
WindowsCodecs.dll!00007ffcaa68f9c6()
wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028eebe50ad0) line 411 C++
wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028eebe50ad0) line 411 C++
wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028eebe50ad0) line 411 C++
wpfgfx_cor3.dll!CSystemMemoryBitmap::UnsafeUpdateFromSource(IWGXBitmapSource * pISource=0x0000028edfb110d0, MilRectU & rcSrc, unsigned int uDstLeft, unsigned int uDstTop=0) line 263 C++
wpfgfx_cor3.dll!CSwBitmapColorSource::FillTextureWithTransformedSource(IWGXBitmapSource * pIBitmapSource=0x0000028edfb110d0) line 706 C++
wpfgfx_cor3.dll!CSwBitmapColorSource::FillTexture() line 584 C++
wpfgfx_cor3.dll!CSwBitmapColorSource::Realize() line 882 C++
wpfgfx_cor3.dll!CSwBitmapColorSource::DeriveFromBitmapAndContext(IWGXBitmapSource * pIBitmap=0x0000028edfc73650, CMatrix<CoordinateSpace::RealizationSampling,CoordinateSpace::DeviceHPC> * pmatBitmapToSampleSpace=0x0000009a90b7b9f0, const CColorSourceCreator * pCSCreator, bool fPrefilterEnabled, float rPrefilterThreshold=1.41421354, IMILResourceCache * pICacheAlternate=0x0000028eebd59cb0, IWGXBitmap * * ppBitmap=0x0000009a90b7b9e0) line 124 C++
wpfgfx_cor3.dll!CColorSourceCreator::GetCS_PrefilterAndResample(IWGXBitmapSource * pIBitmapSource, MilBitmapWrapMode::Enum wrapMode=Extend, const _D3DCOLORVALUE * pBorderColor=0x0000028eebd59d84, const CMatrix<CoordinateSpace::RealizationSampling,CoordinateSpace::DeviceHPC> * pmatTextureHPCToDeviceHPC=0x0000009a90b7bae0, MilBitmapInterpolationMode::Enum interpolationMode=Linear, bool prefilterEnable=false, float prefilterThreshold=1.41421354, IMILResourceCache * pICacheAlternate=0x0000028eebd59cb0, CColorSource * * ppColorSource=0x0000009a90b7bc30) line 1131 C++
wpfgfx_cor3.dll!CSoftwareRasterizer::GetCS_Brush(CMILBrush * pBrush=0x0000028eebd59ca8, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::DeviceHPC> & matWorldHPCToDeviceHPC={...}, const CContextState * pContextState=0x0000028edfacdae8, CColorSource * * ppColorSource=0x0000009a90b7bc30) line 866 C++
wpfgfx_cor3.dll!CSoftwareRasterizer::FillPath(CSpanSink * pSpanSink=0x0000028eebd7a440, CSpanClipper * pSpanClipper=0x0000009a90b7c278, const CContextState * pContextState=0x0000028edfacdae8, const CShapeBase * pShape=0x0000009a90b7c578, const CMatrix<CoordinateSpace::LocalRenderingHPC,CoordinateSpace::DeviceHPC> * pmatShapeToDevice=0x0000028edfacdc80, CMILBrush * pBrush=0x0000028eebd59ca8, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::DeviceHPC> & matWorldToDevice={...}, IMILEffectList * pIEffect=0x0000000000000000, float rComplementFactor=-1.00000000, const TMilRect_<int,tagRECT,MilPointAndSizeL,RectUniqueness::_CMILSurfaceRect_> * prcComplementBounds=0x0000000000000000) line 680 C++
wpfgfx_cor3.dll!CSoftwareRasterizer::FillPathUsingBrushRealizer(CSpanSink * pSpanSink=0x0000028eebd7a440, MilPixelFormat::Enum fmtTarget, DisplayId associatedDisplay, CSpanClipper * pSpanClipper=0x0000009a90b7c278, const CContextState * pContextState=0x0000028edfacdae8, BrushContext * pBrushContext=0x0000000000000000, const CShapeBase * pShape=0x0000009a90b7c578, const CMatrix<CoordinateSpace::LocalRenderingHPC,CoordinateSpace::DeviceHPC> * pmatShapeToDevice=0x0000028edfacdc80, CBrushRealizer * pBrushRealizer=0x0000009a90b7c480, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::DeviceHPC> & matWorldToDevice={...}) line 578 C++
wpfgfx_cor3.dll!CSwRenderTargetSurface::DrawPathInternal(CContextState * pContextState=0x0000028edfacdae8, BrushContext * pBrushContext=0x0000000000000000, const CShapeBase * pShape=0x0000009a90b7c578, const CPlainPen * pPen=0x0000000000000000, CBrushRealizer * pStrokeBrush=0x0000000000000000, CBrushRealizer * pFillBrush=0x0000009a90b7c480) line 841 C++
wpfgfx_cor3.dll!CDrawingContext::FillShapeWithBitmap(IWGXBitmapSource * pIWGXBitmapSource=0x0000028edfc73650, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::LocalRenderingHPC> * pTextureToLocalTransform=0x0000009a90b7c620, CShapeBase * pShape=0x0000009a90b7c578, IMILEffectList * pEffectList=0x0000000000000000, MilBitmapWrapMode::Enum wrapMode=Extend) line 1617 C++
wpfgfx_cor3.dll!CDrawingContext::DrawBitmap(IWGXBitmapSource * pIBitmapSource=0x0000028edfc73650, const MilRectF * prcSource=0x0000009a90b7c728, const MilRectF * prcDest=0x0000009a90b7c6f0, float opacity=1.00000000) line 1479 C++
wpfgfx_cor3.dll!CDrawingContext::DrawImage(CMilSlaveResource * pImage=0x0000028eebda14f0, const MilPointAndSizeD * prcDestinationBase, TMilSlaveValue<MilPointAndSizeD,MILCMD_RECTRESOURCE,52> * pDestRectAnimations=0x0000000000000000) line 1345 C++
wpfgfx_cor3.dll!CMilSlaveRenderData::Draw(IDrawingContext * pIDC=0x0000028edfacda30) line 766 C++
wpfgfx_cor3.dll!CMilVisual::RenderContent(CDrawingContext * pDrawingContext=0x0000028edfacda30) line 1204 C++
wpfgfx_cor3.dll!CDrawingContext::PreSubgraph(int * pfVisitChildren=0x0000009a90b7ca60) line 5076 C++
wpfgfx_cor3.dll!CGraphIterator::Walk(IGraphNode * pRoot, IGraphIteratorSink * pSink=0x0000028edfacda60) line 308 C++
wpfgfx_cor3.dll!CDrawingContext::DrawVisualTree(CMilVisual * pRoot=0x0000028eebd71be0, const _D3DCOLORVALUE * pClearColor=0x0000000000000000, const TMilRect_<float,MilRectF,MilPointAndSizeF,RectUniqueness::NotNeeded> & dirtyRect, bool fDrawingIntoVisualBrush=false) line 4424 C++
wpfgfx_cor3.dll!CDrawingContext::Render(CMilVisual * pRoot=0x0000028eebd71be0, IMILRenderTarget * pIRenderTarget, const _D3DCOLORVALUE * pClearColor=0x0000000000000000, const TMilRect_<float,MilRectF,MilPointAndSizeF,RectUniqueness::NotNeeded> & rcSurfaceBounds={...}, int fFullRender=1, unsigned int uNumInvalidTargetRegions=0, const MilRectF * rgInvalidTargetRegions=0x0000000000000000, bool fCanAccelerateScroll=false, int * pfNeedsFullPresent=0x0000009a90b7ccc8) line 6007 C++
wpfgfx_cor3.dll!CSlaveGenericRenderTarget::Render(bool * pfPresentNeeded=0x0000000000000000) line 88 C++
wpfgfx_cor3.dll!CRenderTargetManager::Render(bool * pfPresentNeeded=0x0000009a90b7ce60) line 339 C++
wpfgfx_cor3.dll!CComposition::Render(bool * pfPresentNeeded=0x0000009a90b7ce60) line 859 C++
wpfgfx_cor3.dll!CComposition::ProcessComposition(bool * pfPresentNeeded=0x0000009a90b7ce60) line 712 C++
wpfgfx_cor3.dll!CComposition::Compose(bool * pfPresentNeeded=0x0000009a90b7ce88) line 805 C++
wpfgfx_cor3.dll!CConnectionContext::PresentAllPartitions() line 506 C++
wpfgfx_cor3.dll!CMilConnection::PresentAllPartitions() line 485 C++
wpfgfx_cor3.dll!WgxConnection_SameThreadPresent(HMIL_CONNECTION__ * hConnection) line 135 C++
PresentationCore.dll!System.Windows.Media.Composition.DUCE.UnsafeNativeMethods.WgxConnection_SameThreadPresent(System.IntPtr pConnection)

从以上代码可以看到是 WPF 的渲染线程的进入了 CWGXBitmapLockState::LockRead 自旋锁等待 WriteableBitmap 解锁

此问题已经报告给官方(嗯,其实就是我),且我已经修复:https://github.com/dotnet/wpf/issues/8134

复现步骤

  1. 创建一个 WriteableBitmap 对象且添加到 Image 控件里面
  2. 调用 WriteableBitmap 的 WritePixels 方法,传入错误的 stride 数值
  3. 立即调用渲染,如使用 RenderTargetBitmap 对 Image 控件进行截图

最小复现 Demo 代码:https://github.com/lindexi/lindexi_gd/tree/20395cade5a79ed40bdd03acf73320994966c691/HawacearkecallLalarnowhallna

原因

这是因为在 WriteableBitmap 的代码实现没有关注到锁的安全性,导致了传入错误的 stride 数值,抛了异常,让 Unlock 没有被调用,从而让锁没有释放。于是渲染线程等待多久,都等不到锁的释放

问题的代码如下:

        private void WritePixelsImpl(
Int32Rect sourceRect,
IntPtr sourceBuffer,
int sourceBufferSize,
int sourceBufferStride,
int destinationX,
int destinationY,
bool backwardsCompat
)
{
// 忽略其他代码 unsafe
{
// 忽略其他代码 Lock(); MILUtilities.MILCopyPixelBuffer(
pDest,
outputBufferSize,
(uint) _backBufferStride.Value,
destBufferBitOffset,
pSource,
inputBufferSize,
(uint) sourceBufferStride,
sourceBufferBitOffset,
(uint) sourceRect.Height,
copyWidthInBits); AddDirtyRect(destinationRect);
Unlock();
}
}

当传入错误的 stride 数值,将会导致 MILUtilities.MILCopyPixelBuffer 抛出异常,从而导致 Unlock 函数没有被正确调用

渲染线程需要等待 WriteableBitmap 的锁释放,但是由于 WriteableBitmap 的 Unlock 因为异常而没有被正确调用,因此渲染线程进入无限等待

WPF 已知问题 传入错误数据给到 WriteableBitmap 可能导致渲染线程锁住的更多相关文章

  1. 对象布局已知时 C++ 对象指针的转换时地址调整

    在我调试和研究 netscape 系浏览器插件开发时,注意到了这个问题.即,在对象布局已知(即对象之间具有继承关系)时,不同类型对象的指针进行转换(不管是隐式的从下向上转换,还是强制的从上到下转换)时 ...

  2. 风口之下,猪都能飞。当今中国股市牛市,真可谓“错过等七年”。 给你一个回顾历史的机会,已知一支股票连续n天的价格走势,以长度为n的整数数组表示,

    转自:http://www.cnblogs.com/ranranblog/p/5845010.html 风口之下,猪都能飞.当今中国股市牛市,真可谓“错过等七年”. 给你一个回顾历史的机会,已知一支股 ...

  3. Delphi 查找标题已知的窗口句柄,遍历窗口控件句柄(转)

    用我的方法来控制其他程序窗体上的窗口控件,必须先了解什么是 回调函数.我的理解是这样的: 回 调函数写出来不是自己的程序去调用的,反而是让其他的东西去调用,比如windows操作系统,比如其他的程序等 ...

  4. ARCgis已知线裁剪已知面

    经常遇到需要在ArcGIS中,根据已知线图层(要素)切分已知面图层(要素).经过研究,利用topology拓扑菜单中的construct features可以实现.具体如下 现有用线图层A.面图层B, ...

  5. Java集合-5. (List)已知有一个Worker 类如下: 完成下面的要求 1) 创建一个List,在List 中增加三个工人,基本信息如下: 姓名 年龄 工资 zhang3 18 3000 li4 25 3500 wang5 22 3200 2) 在li4 之前插入一个工人,信息为:姓名:zhao6,年龄:24,工资3300 3) 删除wang5 的信息 4) 利用for 循

    第六题 5. (List)已知有一个Worker 类如下: public class Worker { private int age; private String name; private do ...

  6. JAVA-集合作业-已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。采用List集合和随机数

    第二题 已知有十六支男子足球队参加2008 北京奥运会.写一个程序,把这16 支球队随机分为4 个组.采用List集合和随机数 2008 北京奥运会男足参赛国家: 科特迪瓦,阿根廷,澳大利亚,塞尔维亚 ...

  7. WCF 已知类型和泛型解析程序 KnownType

    数据协定继承 已知类型和泛型解析程序 Juval Lowy 下载代码示例 自首次发布以来,Windows Communication Foundation (WCF) 开发人员便必须处理数据协定继承方 ...

  8. 【编程题目】n 支队伍比赛,分别编号为 0,1,2。。。。n-1,已知它们之间的实力对比关系,

    36.引用自网友:longzuo(运算)谷歌笔试: 19n 支队伍比赛,分别编号为 0,1,2....n-1,已知它们之间的实力对比关系,存储在一个二维数组 w[n][n]中,w[i][j] 的值代表 ...

  9. 已知树的前序、中序,求后序的java实现&已知树的后序、中序,求前序的java实现

    public class Order { int findPosInInOrder(String str,String in,int position){ char c = str.charAt(po ...

  10. DS Tree 已知后序、中序 => 建树 => 求先序

    注意点: 和上一篇的DS Tree 已知先序.中序 => 建树 => 求后序差不多,注意的地方是在aftorder中找根节点的时候,是从右往左找,因此递归的时候注意参数,最好是拿纸和笔模拟 ...

随机推荐

  1. 在LabVIEW中编程运行可执行程序

    以下文字来自于 https://knowledge.ni.com/KnowledgeArticleDetails?id=kA03q000000YGhVCAW&l=en-US 翻译来自于Chat ...

  2. Android打造万能自定义阴影控件

    目录介绍 01.阴影效果有哪些实现方式 02.实现阴影效果Api 03.设置阴影需要注意哪些 04.常见Shape实现阴影效果 05.自定义阴影效果控件 06.如何使用该阴影控件 07.在recycl ...

  3. 关于数据校验Bean Validation的学习

    1,导相关依赖 2,常用的Validation注解 @NotNull: 标记字段不能为 null @NotEmpty: 标记集合字段不为空(至少要有一个元素) @NotBlank: 标记字段串字段不能 ...

  4. 程序员/后端开发方向Java 跳槽注意事项(简历和面试经验分享)

    程序员/后端开发方向Java 跳槽注意事项(简历和面试经验分享) 应届生面试经验参考:https://www.cnblogs.com/rainbow-1/p/16779048.html 简历: 1.个 ...

  5. 强烈推荐:2024 年12款 Visual Studio 亲测、好用、优秀的工具,AI插件等

    工具类扩展 1. ILSpy 2022 (免费) ILSpy 是 ILSpy 开源反编译器的 Visual Studio 扩展. 是一款开源.免费的.且适用于.NET平台反编译[C#语言编写的程序和库 ...

  6. MySQL插入更新删除数据

    数据插入 插入完整的行 INSERT INTO customers VALUES(NULL, 'Pep E. LaPew', '100 Main Street', 'Los Angeles', 'CA ...

  7. #Manacher,并查集#洛谷 3279 [SCOI2013]密码

    题目 分析 这些回文长度可以提供相等或者不等的信息, 不等的直接连边强制不等,相等用并查集合并连通块, 但是这样判断是\(O(n^2)\),考虑这些回文长度当用Manacher求时, 所有的回文长度都 ...

  8. #Multi-SG#HDU 5795 A Simple Nim

    题目 有\(n\)堆石子,每次可以从一堆中取出若干个或是将一堆分成三堆非空的石子, 取完最后一颗石子获胜,问先手是否必胜 分析 它的后继还包含了分成三堆非空石子的SG函数,找规律可以发现 \[SG[x ...

  9. 深入理解HashMap和TreeMap的区别

    目录 简介 HashMap和TreeMap本质区别 排序区别 Null值的区别 性能区别 共同点 深入理解HashMap和TreeMap的区别 简介 HashMap和TreeMap是Map家族中非常常 ...

  10. OpenHarmony持久化存储UI状态:PersistentStorage

      前两个小节介绍的LocalStorage和AppStorage都是运行时的内存,但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistentStor ...