mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
metal: set contentsScale and handle screen scale factor for retina
This commit is contained in:
@ -2,10 +2,6 @@
|
|||||||
//!
|
//!
|
||||||
//! Open questions:
|
//! Open questions:
|
||||||
//!
|
//!
|
||||||
//! - This requires a "px_scale" uniform to account for pixel scaling
|
|
||||||
//! issues with Retina. I'm not 100% sure why this is necessary and why
|
|
||||||
//! this doesn't happen with OpenGL.
|
|
||||||
//!
|
|
||||||
pub const Metal = @This();
|
pub const Metal = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
@ -84,11 +80,6 @@ const GPUUniforms = extern struct {
|
|||||||
/// This is calculated based on the size of the screen.
|
/// This is calculated based on the size of the screen.
|
||||||
projection_matrix: math.Mat,
|
projection_matrix: math.Mat,
|
||||||
|
|
||||||
/// A scale factor to apply to all pixels given as input (including
|
|
||||||
/// in this uniform i.e. cell_size). This is due to HiDPI screens (Retina)
|
|
||||||
/// mismatch with the window.
|
|
||||||
px_scale: [2]f32,
|
|
||||||
|
|
||||||
/// Size of a single cell in pixels, unscaled.
|
/// Size of a single cell in pixels, unscaled.
|
||||||
cell_size: [2]f32,
|
cell_size: [2]f32,
|
||||||
|
|
||||||
@ -217,7 +208,6 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
.cells = .{},
|
.cells = .{},
|
||||||
.uniforms = .{
|
.uniforms = .{
|
||||||
.projection_matrix = undefined,
|
.projection_matrix = undefined,
|
||||||
.px_scale = undefined,
|
|
||||||
.cell_size = undefined,
|
.cell_size = undefined,
|
||||||
.underline_position = metrics.underline_position,
|
.underline_position = metrics.underline_position,
|
||||||
.underline_thickness = metrics.underline_thickness,
|
.underline_thickness = metrics.underline_thickness,
|
||||||
@ -264,6 +254,13 @@ pub fn finalizeInit(self: *const Metal, window: glfw.Window) !void {
|
|||||||
contentView.setProperty("layer", self.swapchain.value);
|
contentView.setProperty("layer", self.swapchain.value);
|
||||||
contentView.setProperty("wantsLayer", true);
|
contentView.setProperty("wantsLayer", true);
|
||||||
|
|
||||||
|
// Ensure that our metal layer has a content scale set to match the
|
||||||
|
// scale factor of the window. This avoids magnification issues leading
|
||||||
|
// to blurry rendering.
|
||||||
|
const layer = contentView.getProperty(objc.Object, "layer");
|
||||||
|
const scaleFactor = nswindow.getProperty(macos.graphics.c.CGFloat, "backingScaleFactor");
|
||||||
|
layer.setProperty("contentsScale", scaleFactor);
|
||||||
|
|
||||||
if (DevMode.enabled) {
|
if (DevMode.enabled) {
|
||||||
// Initialize for our window
|
// Initialize for our window
|
||||||
assert(imgui.ImplGlfw.initForOther(
|
assert(imgui.ImplGlfw.initForOther(
|
||||||
@ -352,24 +349,29 @@ pub fn render(
|
|||||||
if (critical.screen_size) |screen_size| {
|
if (critical.screen_size) |screen_size| {
|
||||||
const bounds = self.swapchain.getProperty(macos.graphics.Rect, "bounds");
|
const bounds = self.swapchain.getProperty(macos.graphics.Rect, "bounds");
|
||||||
|
|
||||||
// Set the size of the drawable surface to the bounds of our surface.
|
// Scale the bounds based on the layer content scale so that we
|
||||||
self.swapchain.setProperty("drawableSize", bounds.size);
|
// properly handle Retina.
|
||||||
|
const scaled: macos.graphics.Size = scaled: {
|
||||||
|
const scaleFactor = self.swapchain.getProperty(macos.graphics.c.CGFloat, "contentsScale");
|
||||||
|
break :scaled .{
|
||||||
|
.width = bounds.size.width * scaleFactor,
|
||||||
|
.height = bounds.size.height * scaleFactor,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Our drawable surface is usually scaled so we need to figure
|
// Set the size of the drawable surface to the scaled bounds
|
||||||
// out the scalem amount so our pixels are correct.
|
self.swapchain.setProperty("drawableSize", scaled);
|
||||||
const scaleX = @floatCast(f32, bounds.size.width) / @intToFloat(f32, screen_size.width);
|
log.warn("bounds={} screen={} scaled={}", .{ bounds, screen_size, scaled });
|
||||||
const scaleY = @floatCast(f32, bounds.size.height) / @intToFloat(f32, screen_size.height);
|
|
||||||
|
|
||||||
// Setup our uniforms
|
// Setup our uniforms
|
||||||
const old = self.uniforms;
|
const old = self.uniforms;
|
||||||
self.uniforms = .{
|
self.uniforms = .{
|
||||||
.projection_matrix = math.ortho2d(
|
.projection_matrix = math.ortho2d(
|
||||||
0,
|
0,
|
||||||
@floatCast(f32, bounds.size.width),
|
@floatCast(f32, scaled.width),
|
||||||
@floatCast(f32, bounds.size.height),
|
@floatCast(f32, scaled.height),
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
.px_scale = .{ scaleX, scaleY },
|
|
||||||
.cell_size = .{ self.cell_size.width, self.cell_size.height },
|
.cell_size = .{ self.cell_size.width, self.cell_size.height },
|
||||||
.underline_position = old.underline_position,
|
.underline_position = old.underline_position,
|
||||||
.underline_thickness = old.underline_thickness,
|
.underline_thickness = old.underline_thickness,
|
||||||
|
@ -14,7 +14,6 @@ enum Mode : uint8_t {
|
|||||||
|
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
float4x4 projection_matrix;
|
float4x4 projection_matrix;
|
||||||
float2 px_scale;
|
|
||||||
float2 cell_size;
|
float2 cell_size;
|
||||||
float underline_position;
|
float underline_position;
|
||||||
float underline_thickness;
|
float underline_thickness;
|
||||||
@ -60,7 +59,7 @@ vertex VertexOut uber_vertex(
|
|||||||
VertexIn input [[ stage_in ]],
|
VertexIn input [[ stage_in ]],
|
||||||
constant Uniforms &uniforms [[ buffer(1) ]]
|
constant Uniforms &uniforms [[ buffer(1) ]]
|
||||||
) {
|
) {
|
||||||
float2 cell_size = uniforms.cell_size * uniforms.px_scale;
|
float2 cell_size = uniforms.cell_size;
|
||||||
cell_size.x = cell_size.x * input.cell_width;
|
cell_size.x = cell_size.x * input.cell_width;
|
||||||
|
|
||||||
// Convert the grid x,y into world space x, y by accounting for cell size
|
// Convert the grid x,y into world space x, y by accounting for cell size
|
||||||
@ -95,8 +94,8 @@ vertex VertexOut uber_vertex(
|
|||||||
|
|
||||||
case MODE_FG:
|
case MODE_FG:
|
||||||
case MODE_FG_COLOR: {
|
case MODE_FG_COLOR: {
|
||||||
float2 glyph_size = float2(input.glyph_size) * uniforms.px_scale;
|
float2 glyph_size = float2(input.glyph_size);
|
||||||
float2 glyph_offset = float2(input.glyph_offset) * uniforms.px_scale;
|
float2 glyph_offset = float2(input.glyph_offset);
|
||||||
|
|
||||||
// If the glyph is larger than our cell, we need to downsample it.
|
// If the glyph is larger than our cell, we need to downsample it.
|
||||||
// The "+ 3" here is to give some wiggle room for fonts that are
|
// The "+ 3" here is to give some wiggle room for fonts that are
|
||||||
@ -121,7 +120,6 @@ vertex VertexOut uber_vertex(
|
|||||||
|
|
||||||
// Calculate the texture coordinate in pixels. This is NOT normalized
|
// Calculate the texture coordinate in pixels. This is NOT normalized
|
||||||
// (between 0.0 and 1.0) and must be done in the fragment shader.
|
// (between 0.0 and 1.0) and must be done in the fragment shader.
|
||||||
// TODO: do I need to px_scale?
|
|
||||||
out.tex_coord = float2(input.glyph_pos) + float2(input.glyph_size) * position;
|
out.tex_coord = float2(input.glyph_pos) + float2(input.glyph_size) * position;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -158,7 +156,7 @@ vertex VertexOut uber_vertex(
|
|||||||
float2 underline_size = float2(cell_size.x, uniforms.underline_thickness);
|
float2 underline_size = float2(cell_size.x, uniforms.underline_thickness);
|
||||||
|
|
||||||
// Position the underline where we are told to
|
// Position the underline where we are told to
|
||||||
float2 underline_offset = float2(cell_size.x, uniforms.underline_position * uniforms.px_scale.y);
|
float2 underline_offset = float2(cell_size.x, uniforms.underline_position);
|
||||||
|
|
||||||
// Go to the bottom of the cell, take away the size of the
|
// Go to the bottom of the cell, take away the size of the
|
||||||
// underline, and that is our position. We also float it slightly
|
// underline, and that is our position. We also float it slightly
|
||||||
@ -174,7 +172,7 @@ vertex VertexOut uber_vertex(
|
|||||||
float2 strikethrough_size = float2(cell_size.x, uniforms.strikethrough_thickness);
|
float2 strikethrough_size = float2(cell_size.x, uniforms.strikethrough_thickness);
|
||||||
|
|
||||||
// Position the strikethrough where we are told to
|
// Position the strikethrough where we are told to
|
||||||
float2 strikethrough_offset = float2(cell_size.x, uniforms.strikethrough_position * uniforms.px_scale.y);
|
float2 strikethrough_offset = float2(cell_size.x, uniforms.strikethrough_position);
|
||||||
|
|
||||||
// Go to the bottom of the cell, take away the size of the
|
// Go to the bottom of the cell, take away the size of the
|
||||||
// strikethrough, and that is our position. We also float it slightly
|
// strikethrough, and that is our position. We also float it slightly
|
||||||
|
Reference in New Issue
Block a user