Skip to content

Commit b6a565d

Browse files
Copilotphilstopford
andcommitted
Add documentation for viewport DPI fix
Co-authored-by: philstopford <1983851+philstopford@users.noreply.github.com>
1 parent 059179a commit b6a565d

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

VIEWPORT_DPI_FIX.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Viewport High DPI Scaling Fix
2+
3+
## Problem Statement
4+
On scaled high DPI displays, the viewport geometry was not fitted correctly and the view was not centered. Objects would appear smaller than expected, and zoom-to-extents would not properly fit the geometry to the viewport.
5+
6+
## Root Cause
7+
The viewport code was inconsistently using physical pixel dimensions (`RenderWidth`/`RenderHeight`) and logical dimensions (`Width`/`Height`) throughout the rendering pipeline.
8+
9+
On high DPI displays:
10+
- **Logical dimensions** (`Width`/`Height`): The control size in logical pixels (e.g., 800x600)
11+
- **Physical dimensions** (`RenderWidth`/`RenderHeight`): The actual framebuffer size in physical pixels, scaled by DPI (e.g., 1600x1200 on a 2x DPI display)
12+
13+
The issue was that some parts of the code used physical dimensions while others used logical dimensions, causing:
14+
1. The orthographic projection to be calculated based on physical pixels, making the view frustum too large in world space
15+
2. The zoom calculations to divide world space extents by physical pixels, making objects appear smaller
16+
3. Mouse coordinate conversions to scale by DPI when they should use logical coordinates
17+
18+
## Solution
19+
Changed the viewport to consistently use **logical dimensions** throughout:
20+
21+
### Files Modified
22+
23+
#### 1. `Eto/Eto.VeldridSurface/VeldridDriver_Draw.cs`
24+
- **Draw() method**: Changed orthographic projection to use `Surface.Width`/`Surface.Height` instead of `Surface.RenderWidth`/`Surface.RenderHeight`
25+
- **drawGrid() method**: Changed grid extent calculations to use logical dimensions
26+
- **drawAxes() method**: Changed axis extent calculations to use logical dimensions
27+
28+
#### 2. `Eto/Eto.VeldridSurface/VeldridDriver_Public.cs`
29+
- **zoomExtents() method**: Changed zoom level calculations to use `Surface.Width`/`Surface.Height` instead of `Surface.RenderWidth`/`Surface.RenderHeight`
30+
31+
#### 3. `Eto/Eto.VeldridSurface/VeldridDriver_Conversions.cs`
32+
- **ScreenToWorld() method**: Changed coordinate conversions to use logical dimensions
33+
- **WorldToScreen() method**: Changed coordinate conversions to use logical dimensions
34+
35+
#### 4. `Eto/Eto.VeldridSurface/VeldridDriver_Handlers.cs`
36+
- **dragHandler() method**: Removed scaling of mouse coordinates by `LogicalPixelSize`
37+
- **selectByClick() method**: Removed scaling of mouse coordinates by `LogicalPixelSize`
38+
39+
## Technical Details
40+
41+
### Before (Incorrect)
42+
```csharp
43+
// Orthographic projection using physical pixels
44+
float left = ovpSettings.getCameraX() - (float)Surface!.RenderWidth / 2 * zoom;
45+
float right = ovpSettings.getCameraX() + (float)Surface!.RenderWidth / 2 * zoom;
46+
47+
// Zoom calculation using physical pixels
48+
float zoomLevel_x = dX / Surface!.RenderWidth;
49+
50+
// Mouse coordinates scaled by DPI
51+
PointF scaledLocation = e.Location * Surface!.ParentWindow.LogicalPixelSize;
52+
```
53+
54+
### After (Correct)
55+
```csharp
56+
// Orthographic projection using logical dimensions
57+
float left = ovpSettings.getCameraX() - (float)Surface!.Width / 2 * zoom;
58+
float right = ovpSettings.getCameraX() + (float)Surface!.Width / 2 * zoom;
59+
60+
// Zoom calculation using logical dimensions
61+
float zoomLevel_x = dX / Surface!.Width;
62+
63+
// Mouse coordinates in logical pixels (no scaling needed)
64+
PointF scaledLocation = e.Location;
65+
```
66+
67+
## Why This Works
68+
69+
1. **Orthographic Projection**: By using logical dimensions, the view frustum size in world space remains consistent regardless of DPI scaling. The GPU automatically handles the mapping to the physical framebuffer.
70+
71+
2. **Zoom Calculations**: Using logical dimensions ensures that the same world space extent results in the same zoom level, regardless of DPI.
72+
73+
3. **Coordinate Conversions**: Mouse events report coordinates in logical pixels, and by using logical dimensions throughout, we maintain consistency between input coordinates and rendered coordinates.
74+
75+
4. **Framebuffer**: The framebuffer size (`RenderWidth`/`RenderHeight`) is still correctly set by the platform handlers (GTK/WPF) to match the physical pixel count, ensuring sharp rendering on high DPI displays.
76+
77+
## Testing
78+
- All 437 existing unit tests pass
79+
- Build completes without errors
80+
- Code is ready for manual testing on different DPI settings (1x, 1.5x, 2x, etc.)
81+
82+
## Expected Behavior After Fix
83+
- Geometry is properly fitted to the viewport on zoom-to-extents
84+
- View is correctly centered on the geometry
85+
- Zoom levels are consistent across different DPI settings
86+
- Mouse panning and selection work correctly on high DPI displays
87+
- Grid and axes render at appropriate scales

0 commit comments

Comments
 (0)