wpf
wpf copied to clipboard
Shader Effect broken at large sizes?
Description
Use a ShaderEffect similar to the example from the API docs. Use it on a large visual (e.g. a large rectangle or image.) Problem: Not the entire visual is drawn, if any effect is applied. (It does not matter what the effect does.)
Reproduction Steps
(Or use the attached full project below.)
With this C# code:
public class CustomElement : FrameworkElement
{
public VisualCollection visuals;
public CustomElement()
{
this.visuals = new VisualCollection(this);
// Bug only manifests at a certain size:
var rect = new Rect() { X = 300, Y = -3000, Width = 3400, Height = 3400 };
void _DrawRectWithEffect(Rect rect, Effect? effect)
{
var visual = new DrawingVisual() { Effect = effect };
using (var drawingContext = visual.RenderOpen())
{
drawingContext.DrawRectangle(Brushes.Red, pen: null, rect);
}
this.visuals.Add(visual);
}
// Works correctly: red
_DrawRectWithEffect(rect, effect: null);
var effect = new TestEffect();
// Bug: Only partially drawn: magenta
_DrawRectWithEffect(rect, effect: effect);
}
protected override int VisualChildrenCount => visuals.Count;
protected override Visual GetVisualChild(int index) => visuals[index];
public int Count => VisualChildrenCount;
public Visual this[int index] => GetVisualChild(index);
}
public class TestEffect : ShaderEffect
{
private static Uri _MakePackUri(string relativeFile)
{
var assembly = typeof(TestEffect).Assembly;
var assemblyShortName = assembly.ToString().Split(',')[0];
var uriString = $"pack://application:,,,/{assemblyShortName};component/{relativeFile}";
return new Uri(uriString);
}
public TestEffect()
{
PixelShader = new PixelShader() { UriSource = _MakePackUri("TestEffect.fx.ps") };
UpdateShaderValue(InputProperty);
}
public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof(TestEffect), 0);
public Brush Input { get => (Brush)GetValue(InputProperty); set => SetValue(InputProperty, value); }
}
And this trivial pixel shader file TestEffect.ps
:
// Build shader with fxc.exe:
// "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\fxc.exe" /O0 /Fc /Zi /T ps_2_0 /Fo TestEffect.fx.ps TestEffect.ps
sampler2D implicitInput : register(s0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
return float4(1.0, 0.0, 1.0, 1.0); // Fixed color magenta #FF00FF
}
And this trivial XAML file MainWindow.xaml
:
<Window x:Class="WpfShaderTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfShaderTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
WindowState="Maximized">
<local:CustomElement />
</Window>
Compile and run it.
Expected behavior
The two visuals are drawn with the same rectangle coordinates, so they should cover the same area.
The second (magenta) visual with the effect should be positioned correctly and fully / exactly cover the first (red) visual.
Actual behavior
The first (red) rectangle (without effect) is positioned correctly. The second (magenta) rectangle (with effect) is positioned slightly incorrectly, so the red rectangle is not fully covered.
Regression?
Not sure.
Known Workarounds
None.
Impact
The real use case is an image viewer for very large images with deep zoom levels. This bug also happens when drawing images, not only when drawing rectangles. The image is then not completely displayed! Some parts are just missing! And with larger sizes / deeper zoom levels more and more of the image is missing.
So the impact is that it's impossible to rely on WPF for drawing large images with an effect applied. The effects are required to perform interactive image manipulations (thresholding, color space conversion, ...)
Configuration
.NET 8 Windows 11 x64 I assume this is not specific to that configuration.
Other information
None