A reality when working with hardware accelerated graphics is that sometimes bad things can happen. Someone might remove the graphics device, upgrade the graphics driver, put the GPU into an infinite loop or upset the driver in some way. These can all cause the GPU to lose track of resources that you’ve created on it. As an app developer it is not possible to prevent any of this from happening, but, unless you like seeing bug reports about your app crashing or displaying blank screens, it is up to you to ensure your app handles it.
This can be tedious and tricky to get right!
One of the challenges we set ourselves for Win2D was to relieve app developers from the burden of lost device handling. This requirement has shaped the design of the API from the very beginning.
This series attempts to give an insight into how we went about designing this aspect of Win2D, from whiteboard to a completed solution that turned out to be incomplete to one that really was complete.
One possible approach that has worked well for retained mode APIs (such as XAML) is to entirely virtualize the resource that might get lost – to the extent that the app isn’t even aware that devices can be lost. So, if you have a bitmap, then the system keeps a copy of the image in CPU memory that can be used to recreate the GPU resource if necessary. This approach doesn’t scale too well for an immediate mode API where the image naturally exists only on the GPU. We’d have to be continuously copying the image from GPU memory to CPU memory for safe keeping. Every image loaded from disk would exist in memory twice, just to support the exceptional event of a device lost.
Things can get even more hairy: what would have to happen if you created a render target and drew on it while there’s no valid device? Should the system queue up the API calls so it can replay them when the device reappears? What happens then if you call GetPixelColors()?
We thought about this for a while, and it started to sound like it’d have a very high overhead and be really complicated. So we didn’t do that.
(Aside: for some types virtualization made sense since we were already performing some kind of virtualization for other reasons – CanvasStrokeStyle, CanvasTextFormat and effects are all device independent. The GPU resources backing these are “realized” on demand as they’re used. Types that behave like this are easy to spot since they are constructed without a device.)
Instead we decided, at the lowest level, to do nothing. It doesn’t get much simpler than that! The device dependent Win2D types (CanvasSolidColorBrush, CanvasBitmap, CanvasRenderTarget, CanvasSwapChain etc.) make no attempt to handle device lost. Instances of these types are all created against a device and, if the device is lost, operations on them will fail. At this point the object is no longer useful and a new one must be created.
It seems that we’re back where we started. In the next part I’ll describe our first attempt to manage resource creation via CanvasControl.