For time to time we get into a situation where the entire GUI freezes. When investigating the application dump, the freeze is found to be located in the constructor of BitmapContext, or rather inside a .Net-method called as a result of the constructor. The area of interest seems to be the use of the BackBuffer property of WriteableBitmap. This in turn ends up waiting for an event inside AcquireBackBuffer (the ManualResetEvent variable is called _copyCompletedEvent). On MSDN Microsoft states that one should call the Lock- and Unlock-methods when updating the back buffer ( http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.backbuffer(v=vs.110).aspx). I seems like this is not always the case in the SciChart source code, but I’m unable to determine if this can cause the lock.
mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext) + 0x2b bytes
mscorlib.dll!System.Threading.WaitHandle.WaitOne(System.TimeSpan timeout, bool exitContext) + 0x6e bytes
PresentationCore.dll!System.Windows.Media.Imaging.WriteableBitmap.AcquireBackBuffer(System.TimeSpan timeout, bool waitForCopy) + 0x37 bytes
> PresentationCore.dll!System.Windows.Media.Imaging.WriteableBitmap.TryLock(System.Windows.Duration timeout) + 0x10c bytes
Abt.Controls.SciChart.Wpf.2.2.dll!System.Windows.Media.Imaging.BitmapContext.BitmapContext(System.Windows.Media.Imaging.WriteableBitmap c1505829363987065354c2524c41152e4) + 0xa6 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!A.c6e54b260f3e614b850e733a2116b4878.c6e54b260f3e614b850e733a2116b4878(System.Windows.Controls.Image c9f773d96fd8c18abf23cfdb63f49fe85, System.Windows.Media.Imaging.WriteableBitmap ce748be01e2f961df40736b3a402f6c8a, Abt.Controls.SciChart.Rendering.Common.SizeF cacf0a4a95d1143f0c11e20d4508e7132, A.c1712c9b21919ce890684525c1002f4a7 c10fe853bbcfc02fab8756c021ff9007b) + 0xb7 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!A.ccd89db9dd1768b91d35b2d2bb15f2f07.cb398c793fba70ab574d5eb126a856bcf(System.Windows.Media.Imaging.WriteableBitmap c1505829363987065354c2524c41152e4, System.Windows.Controls.Image c9f773d96fd8c18abf23cfdb63f49fe85, A.c1712c9b21919ce890684525c1002f4a7 c10fe853bbcfc02fab8756c021ff9007b) + 0x61 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!Abt.Controls.SciChart.Visuals.Axes.AxisBase.OnDraw(Abt.Controls.SciChart.Rendering.Common.IRenderContext2D renderContext, Abt.Controls.SciChart.Visuals.RenderableSeries.IRenderPassData renderPassData) + 0x82 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!A.cda144392e546b245ef5bb1ee71f22b3a.c6a7d2b5be124728330bbf562594a9bb9(Abt.Controls.SciChart.Visuals.ISciChartSurface c17037e8328cd0abc02d2a6957dfa450c, Abt.Controls.SciChart.RenderPassInfo c16b8d70d2b6ecad8f9fca7ac3f5177b8, Abt.Controls.SciChart.Rendering.Common.IRenderContext2D c41db0419b661c8ac05a2aa6a1ea66092) + 0x66 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!A.cda144392e546b245ef5bb1ee71f22b3a.RenderLoop(Abt.Controls.SciChart.Rendering.Common.IRenderContext2D renderContext) + 0x155 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!Abt.Controls.SciChart.Visuals.SciChartSurface.ca065c0b671221e0e603d0e9bf2792494() + 0x3e bytes
Abt.Controls.SciChart.Wpf.2.2.dll!Abt.Controls.SciChart.Visuals.SciChartSurface.cc0485eb45a8d4f00283729754056deb8() + 0x3e bytes
Abt.Controls.SciChart.Wpf.2.2.dll!Abt.Controls.SciChart.Visuals.SciChartSurface.OnRenderSurfaceDraw(object sender, Abt.Controls.SciChart.Rendering.Common.DrawEventArgs e) + 0x84 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!Abt.Controls.SciChart.Rendering.Common.RenderSurfaceBase.OnDraw() + 0x92 bytes
Abt.Controls.SciChart.Wpf.2.2.dll!Abt.Controls.SciChart.Rendering.Common.RenderSurfaceBase.OnCompositionTargetRendering(object sender, System.EventArgs e) + 0x1c bytes
Have you had any experience with this?
Bjørn Terje Svennes
- You must login to post comments
Yes, we are calling lock there. However that will only block if called on more than one thread at one time. In SciChart codebase we only ever call BitmapContext constructors on the UI thread.
So do you think that there is a mismatch between Lock/Unlock calls for the Bitmap? Or we need to call Lock/Unlock every time?
I can review this code. It’s quite deep in the guts of SciChart and so far seems to have worked, but you never know …
Hi Andrew. We've been looking for mismatches between Lock/Unlock calls, but it seems your code is impeccable at this point :-) If not we would surly get into this situation more often I think. We've disassembled the .Net code behind Lock and, as described above, found that AcquireBackBuffer is the culprit. .Net uses some sort of batch operation to allocate the back buffer. AcquireBackBuffer waits for this operation to complete by calling WaitOne on a ManualResetEvent. The WaitOne has a timeout set to forever and will never get released if the batch operation doesn't complete. It also seems that AcquireBackBuffer only happens once (it checks a lock counter for value equal to 0), so if there was a problem with multiple calls to Lock it should result in this behavior. A way of improving the SciChart code would perhaps be to change the call to Lock with TryLock with some non-infinite timeout. But then you had to handle situations where the BackBuffer isn't allocated. MSDN doesn't say anything about this being normal behavior. I'm not sure where to go from here. I was curious to if you have seen this situation before, but as you say it seems to have worked. Bjørn Terje
Ah this rings a bell. We did at one point experiment with multi-threaded rendering, e.g. drawing different series or different chart surfaces on different threads. We experienced the lock issue you talked about and TryLock 'resolved it'. I say 'resolved' as what happened was every 1000 frames or so you dropped a frame (it plain didn't render). This ideally required a retry later on. The conclusion we came to is the Bitmap.Lock method (in the .NET framework itself) was a bit dodgy. Whether that conclusion is correct or not I don't know. Another thing we do if you look at BitmapContext is we only lock/unlock the underlying bitmap once. Is this a problem do you think?
I don't think calling Lock/Unlock once is necessarily a problem. If I have understood your code correctly, the BitmapContext only lives for one render cycle. MSDN states that Lock should be called before updating the BackBuffer and and Unlock when the update has finished. This fits with how you are using it. Once Unlock is called the back buffer is flushed to the front buffer. I guess the question at hand is for how long it takes to do one render cycle. MSDN states the following:
The UI thread can block when the render thread acquires a lock on the back buffer to copy it forward to the front buffer. If the latency from this block is too long, use the TryLock method to wait for a short time and then unblock the UI thread to perform other tasks while the back buffer is locked.Do you have any ideas on for how long the lock is kept in BitmapContext? I've never used WriteableBitmap and I'm a bit on thin ice now. But from what you are saying, you had an issue every 1000 frames or so. This fits with our situation too. It happens rarely, but when it does the entire application locks and needs to be terminated from TaskManager. This is not very pleasant for the customers. Bjørn Terje
I should stress, we only saw this 1:1000 locking if the drawing code itself was being called on a background thread (which we don't do in Production SciChart). Can you check which thread you are running on? Also you are the only person to see this issue (apart from us when we experimented with background threads). To answer your question, the BitmapContext is a struct and is passed around between draw methods. It is created once at the start of a render operation and disposed at the end of a render operation. Since it is a struct the constructor is called on each time it is passed to a draw method, however, we avoid the need to Bitmap.Lock by use of the code in the ctor. If everything is single threaded it should just work, so I'm stumped for now? :-? Andrew
I see. The drawing code is being called on the main thread. We are adding samples to the chart on another thread, but this shouldn't influence were rendering occurs. We will continue to monitor the situation. For now I don't have any answer. I'm not sure what is so special about our environment, except maybe for the fact that we use Windows XP Embedded. I haven't heard that is that much different from a normal Windows XP. And the .Net 4.0 package installed is the same as for any other XP. Thank you very much for your input so far. Bjørn Terje
- You must login to post comments
Please login first to submit.