dotnet / dotnet/wpf

Shader Effect broken at large sizes?

Open
#9,391 12 comments 0 reactions 0 assignees View on GitHub
Investigate
Dominant language
C#
Stars
7.7k
Forks
1.3k
Avg merge
1d 11h
Merged PRs (30d)
61

Description

### Description

Use a ShaderEffect similar to the [example from the API docs](https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.effects.shadereffect).
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`:
```

```

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

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.