microsoft / Win2D

Win2D is an easy-to-use Windows Runtime API for immediate mode 2D graphics rendering with GPU acceleration. It is available to C#, C++ and VB developers writing apps for the Windows Universal Platform (UWP). It utilizes the power of Direct2D, and integrates seamlessly with XAML and CoreWindow.
http://microsoft.github.io/Win2D
Other
1.79k stars 285 forks source link

D2D bug: CanvasVirtualBitmap makes a mess of PNGs in certain situations. #791

Open benstevens48 opened 3 years ago

benstevens48 commented 3 years ago

This is a bug in ID2D1ImageSource and ID2D1TransformedImageSource presumably. Here is what is possible to produce by combining a CanvasVirtualBitmap created from a PNG, a Transform2D effect with non-linear interpolation, a Border effect to tile it, and a Blend effect to blend with a base image.

png-scale-tiling-issue

The cracked texture (which is the CanvasVirtualBitmap PNG) should be uniformly tiled across the image, but there are a load of artefacts/garbage displayed.

This situation happens in two circumstances that I've noticed. The first is simply CanvasVirtualBitmap (from PNG) -> Transform2DEffect with non-linear interpolation (not sure if it ever occurs with linear as well) -> render. But it only happens in this case if the Transform2DEffect is being reused having previously been used with a different image source. The second situation, which is more complicated, is the one shown above, which is CanvasVirtualBitmap (from PNG) -> Transform2DEffect with non-linear interpolation (not sure if it ever occurs with linear as well) -> tile using BorderEffect -> blend with base image -> render. This seems to occur even without changing the image source, but it is necessary to change the scale of the Transform2DEffect without disposing and re-creating the effect for it to happen.

I did eventually find an acceptable but inconvenient workaround, which is to add an effect before the transform effect. However, this effect cannot be bypassed due to being the identity, therefore I had to create my own identity pixel shader effect in order for this to work (and thanks for including the PixelShaderEffect in Win2D by the way, with instructions on how to compile the shader - it's really great). So it seems like perhaps the transform/tile effect is copying pixels from a surface that it expects to be filled from the CanvasVirtualBitmap but for some reason these pixels are not filled in (and are just garbage/whatever was there before) unless I add an effect before the transform effect which forces the pixels to be produced from the CanvasVirtualBitmap. This only happens with PNGs, not JPEGs. I don't know if it happens with other image types. It's also possible to workaround using CanvasBitmap instead, but the performance of CanvasVirtualBitmap can actually be much better and supports very large images.

Please can you pass this issue on to the D2D team? I think it's important to fix this given how important the PNG image format is. I might be able to produce a minimal repro if required, depending on how much time I have available.

shawnhar commented 3 years ago

Thanks, I will pass this along.

MilesCohen-MS commented 3 years ago

I wonder if you could share a repro of this issue? If so that would be very helpful.

benstevens48 commented 3 years ago

@MilesCohen-MS - I had a hard time reproducing it as convincingly as in the above picture, but I did eventually get it to happen. Attached is a sample project. Run the project and adjust the bottom slider so the image just fits in the window. Then move the top slider around to adjust the texture size (don't make it too small else it will be very inefficient since I haven't enabled CacheOutput). I found that between scale 1.01 and 1.1 (approx), I got a rectangle artefact in the top right of the image. To test with another image, click the 'Switch background' button, then I get an artefact in the bottom left under the same conditions. It's strange that I wasn't able to get so many artefacts as before though.

VirtualBitmapRenderTest2.zip

MilesCohen-MS commented 3 years ago

Thanks for the repro. I can confirm that you are hitting a bug in Direct2D here, caused by a faulty optimization of this effect graph. It sounds like you have a workaround here already, but for what it's worth I also noticed that if you set the BorderMode to Soft on the textureScale effect (the first effect applied to the texture), then the bug no longer repros.

benstevens48 commented 3 years ago

Thanks for looking into this. Any idea which OS version it will be fixed in? I'll try to remember to check if the original bug is fixed once this OS version is released as a stable version (although I will have to remove my workaround to check it, which may or may not be practical).

MilesCohen-MS commented 3 years ago

Unfortunately, I'm not sure when/if this one will be fixed.

benstevens48 commented 3 years ago

Thanks for the reply.

I can understand that this might not be a priority, but it would be quite disappointing if it wasn't fixed. If it's a faulty optimization of the effect graph presumably there are other situations in which it could apply. I could understand not fixing it if the API was deprecated or likely to be replaced soon, but as far as I am aware Direct2D is still the foundation of Windows graphics and is supposed to be a solid foundation to build on, so I would think that any bugs in Direct2D ought to be fixed. I suppose maybe there is a concern of introducing another regression with the fix, but in my opinion this just means being extra careful.

At the very least, if bugs like this aren't going to be fixed, they should be documented somewhere so that developers can work around them without wasting a lot of time diagnosing them.