Skip to content

Commit 8778ed4

Browse files
committed
fix scaling for high dpi
1 parent aceda35 commit 8778ed4

6 files changed

Lines changed: 29 additions & 10 deletions

File tree

webgpu/camera.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class CameraUniforms(UniformBase):
1818
("aspect", ct.c_float),
1919
("width", ct.c_uint32),
2020
("height", ct.c_uint32),
21-
("padding", ct.c_uint32),
21+
("dpr", ct.c_float),
2222
]
2323

2424
def update(self, transform, canvas):
@@ -81,6 +81,7 @@ def update(self, transform, canvas):
8181
self.rot_mat[:] = rot_mat4.transpose().flatten()
8282
self.width = canvas.width
8383
self.height = canvas.height
84+
self.dpr = float(getattr(canvas, 'dpr', 1.0))
8485
self.update_buffer()
8586

8687
return model_view_proj, model_view

webgpu/canvas.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ def __init__(self, device, canvas, multisample_count=4):
284284
)
285285
self.multisample = MultisampleState(count=multisample_count)
286286

287+
self.dpr = 1.0 # updated in resize(); kept as attribute for camera uniforms
288+
287289
self.update_html_canvas(canvas)
288290

289291
def __del__(self):
@@ -409,6 +411,7 @@ def resize(self):
409411
canvas.width = width
410412
canvas.height = height
411413

414+
self.dpr = dpr
412415
device = self.device
413416

414417
self.destroy_textures()

webgpu/engine/camera.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ class Camera {
229229
* aspect (f32) [320..323]
230230
* width (u32) [324..327]
231231
* height (u32) [328..331]
232-
* padding (u32) [332..335]
232+
* dpr (f32) [332..335] device-pixel ratio (0 in old blobs)
233233
*/
234234
updateUniforms(width, height) {
235235
if (height === 0) return null;
@@ -301,7 +301,7 @@ class Camera {
301301
f32[80] = aspect;
302302
u32[81] = width;
303303
u32[82] = height;
304-
u32[83] = 0;
304+
f32[83] = (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
305305

306306
return buf;
307307
}

webgpu/engine/input.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,19 @@ class InputHandler {
7070
// Suppress pointer-based rotation/pan while a multi-touch gesture is active
7171
if (this._activeTouches.size >= 2) return;
7272

73+
// On high-DPI screens (e.g. macOS Retina) the OS reports pointer movement
74+
// in device pixels, which are then divided by devicePixelRatio to produce
75+
// CSS-pixel deltas. Multiply back by DPR so that the same physical finger
76+
// movement always produces the same camera rotation/pan, regardless of
77+
// the screen's pixel density.
78+
const dpr = window.devicePixelRatio || 1;
7379
const t = this.camera.transform;
7480
if (this._isRotating) {
75-
t.rotate(0.3 * ev.movementY, 0.3 * ev.movementX);
81+
t.rotate(0.3 * dpr * ev.movementY, 0.3 * dpr * ev.movementX);
7682
this.camera._notify();
7783
this.onRender();
7884
} else if (this._isPanning) {
79-
t.translate(0.01 * ev.movementX, -0.01 * ev.movementY);
85+
t.translate(0.01 * dpr * ev.movementX, -0.01 * dpr * ev.movementY);
8086
this.camera._notify();
8187
this.onRender();
8288
}

webgpu/shaders/camera.wgsl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ struct CameraUniforms {
77
aspect: f32,
88
width: u32,
99
height: u32,
10-
11-
padding: u32,
10+
// Device-pixel ratio of the canvas (e.g. 2.0 on Retina). Used by the font
11+
// shader to convert device-pixel dimensions to CSS-pixel dimensions so that
12+
// font sizes are DPI-independent. Zero in old blobs — treated as 1.0.
13+
dpr: f32,
1214
};
1315

1416
@group(0) @binding(0) var<uniform> u_camera : CameraUniforms;

webgpu/shaders/font.wgsl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,18 @@ fn fontGetSizeInTexture() -> vec2<f32> {
3535
}
3636

3737
fn fontGetSizeOnScreen() -> vec3<f32> {
38+
// u_camera.dpr is 0.0 in old exported blobs — treat that as 1.0 (no
39+
// correction) so existing assets are not broken.
40+
let dpr = max(1.0, u_camera.dpr);
41+
// u_camera.width/height are device pixels; divide by dpr to get CSS pixels
42+
// so that font_size values behave like CSS pixels on every screen density.
43+
let css_width = f32(u_camera.width) / dpr;
44+
let css_height = f32(u_camera.height) / dpr;
3845
let size = f32(u_font.size * u_font.size_scaling);
39-
let h = 2.0 * size / f32(u_camera.height);
40-
let size_horizontal = 2.0 * size / f32(u_camera.width);
46+
let h = 2.0 * size / css_height;
47+
let size_horizontal = 2.0 * size / css_width;
4148
let w = size_horizontal * u_font.aspect;
42-
let shift = 2.0 * u_font.advance * u_font.size / f32(u_camera.width);
49+
let shift = 2.0 * u_font.advance * u_font.size / css_width;
4350
return vec3<f32>(w, h, shift);
4451
}
4552

0 commit comments

Comments
 (0)