WPF Rendering Architecture
Rendering Pipeline Overview
User Input → Property Change → InvalidateXxx() ↓ ┌───────────────┐ │ Measure Pass │ → Determines DesiredSize ├───────────────┤ │ Arrange Pass │ → Determines ActualSize/Position ├───────────────┤ │ Render Pass │ → OnRender() / DrawingContext └───────────────┘ ↓ Composition Tree → MILCore → DirectX → GPU
- Layout Passes
Invalidation Methods
Method Triggers Use When
InvalidateMeasure()
Measure + Arrange + Render Size might change
InvalidateArrange()
Arrange + Render Position might change
InvalidateVisual()
Render only Visual appearance change
FrameworkPropertyMetadata Flags
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(MyControl), new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
Flag Effect
AffectsMeasure
Triggers Measure pass
AffectsArrange
Triggers Arrange pass
AffectsRender
Triggers Render pass
AffectsParentMeasure
Parent re-measures
AffectsParentArrange
Parent re-arranges
Custom Control Layout
protected override Size MeasureOverride(Size availableSize) { // Calculate desired size based on content double width = Math.Min(200, availableSize.Width); double height = Math.Min(100, availableSize.Height); return new Size(width, height); }
protected override Size ArrangeOverride(Size finalSize) { // Position children within finalSize foreach (UIElement child in Children) { child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height)); } return finalSize; }
protected override void OnRender(DrawingContext dc) { dc.DrawRectangle(Background, null, new Rect(RenderSize)); }
- Hardware Acceleration
Rendering Tiers
Tier GPU Memory Features
0 < 30MB Software rendering
1 30-120MB Partial hardware (no PS 2.0)
2 ≥ 120MB Full hardware acceleration
int tier = RenderCapability.Tier >> 16; // 0 = Software, 1 = Partial, 2 = Full GPU
Software Fallback Conditions
WPF falls back to software rendering when:
-
GPU memory insufficient
-
Texture > 8192px
-
Remote Desktop (RDP) session
-
AllowsTransparency="True" on Window
-
Legacy BitmapEffect used
RenderOptions Optimization
// For images that will be scaled RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.LowQuality);
// For pixel-perfect lines RenderOptions.SetEdgeMode(element, EdgeMode.Aliased);
// For tiled brushes RenderOptions.SetCachingHint(brush, CachingHint.Cache); RenderOptions.SetCacheInvalidationThresholdMinimum(brush, 0.5); RenderOptions.SetCacheInvalidationThresholdMaximum(brush, 2.0);
- Performance Optimization
Batch Updates Pattern
// Bad: Multiple layout passes for (int i = 0; i < 100; i++) { items[i].Width = newWidths[i]; // Each triggers layout }
// Good: Single layout pass using (Dispatcher.DisableProcessing()) { for (int i = 0; i < 100; i++) { items[i].Width = newWidths[i]; } } // Layout happens once here
Common Performance Issues
Problem Cause Solution
Slow scrolling Deep Visual Tree Virtualization
Layout thrashing Frequent InvalidateMeasure Batch updates
Slow startup Complex Grid Simplify layout
Memory growth BitmapEffect Use Effect class
- Tier-Adaptive Rendering
public void ConfigureForTier() { int tier = RenderCapability.Tier >> 16;
if (tier < 2)
{
// Disable expensive effects
DisableDropShadows();
DisableAnimations();
UseSimplifiedVisuals();
}
}