More mathematically sound approach, does a much better job of matching
the appearance of non-linear blending. Removed `experimental` from name
because it's not really an experiment anymore.
This fixes a regression introduced by the rework of this area before
during the color space changes. It seems like the original intent of
this code was the behavior it regressed to, but it turns out to be
better like this.
NEEDS REVIEW
continuation of #5037resolves#4729
renders all shaders to the default buffer and then copies it to the
designated custom shader texture.
this is a draft pr because:
- it introduces a new shader "pipeline" which doesnt fit in with how the
system was designed to work (which is only rendering to the fbo)
- im not sure if this is the best way to achieve shaders being able to
sample their output while also drawing to the screen. the cusom fbo
(previous implementation) was useful in that it modularized the custom
shader stage in rendering
---------
Co-authored-by: Mitchell Hashimoto <m@mitchellh.com>
This significantly improves the robustness of the renderers since it
prevents synchronization issues from causing memory corruption due to
out of bounds read/writes while building the cells.
TODO: when viewport is narrower than renderer grid size, fill blank
margin with bg color- currently appears as black, this only affects
DECCOLM right now, and possibly could create single-frame artefacts
during poorly managed resizes, but it's not ideal regardless.
fixes#4729
allows the shaders to sample each other via the fbo texture.
also, a better example would use the full screen e.g.:
"behind.glsl"
```glsl
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
fragColor = vec4(fragCoord/iResolution.xy, 0.0, 1.0);
}
```
"infront.glsl"
```glsl
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
fragColor = texture(iChannel0, fragCoord/iResolution.xy);
}
```
`ghostty --custom-shader=behind.glsl --custom-shader=infront.glsl`
|before|after|
|-|-|
|

|
|
We were returning bg colors when we shouldn't have since when we have
background color transparency we need to return any bg color cells as
fully transparent rather than their actual color.
By using the `CAMetalLayer`'s `backgroundColor` property instead of
drawing the background color in our shader, it is always stretched to
cover the full surface, even when live-resizing, and it doesn't require
us to draw a frame for it to be initialized so there's no transparent
flash when a new surface is created (as in a new split/tab).
This commit also allows for hot reload of `background-opacity`,
`window-vsync`, and `window-colorspace`.
This commit is quite large because it's fairly interconnected and can't
be split up in a logical way. The main part of this commit is that alpha
blending is now always done in the Display P3 color space, and depending
on the configured `window-colorspace` colors will be converted from sRGB
or assumed to already be Display P3 colors. In addition, a config option
`text-blending` has been added which allows the user to configure linear
blending (AKA "gamma correction"). Linear alpha blending also applies to
images and makes custom shaders receive linear colors rather than sRGB.
In addition, an experimental option has been added which corrects linear
blending's tendency to make dark text look too thin and bright text look
too thick. Essentially it's a correction curve on the alpha channel that
depends on the luminance of the glyph being drawn.
This refactor enables two very significant improvements to our font
handling, which I will be implementing next:
1. Automatically adjust size of fallback faces to better align with the
primary face, a la CSS
[`font-size-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size-adjust).
[^1]
2. Move glyph resizing/positioning out of GPU-land and in to
`renderGlyph` and apply alignment/resizing rules from the nerd fonts
patcher[^2] to the glyphs at rasterization time, so that we can ensure
exact cell fits and swap out our embedded JB Mono with an unpatched
version with a separate dedicated symbols-only nerd font.
In addition to being necessary prep work for those two changes, this PR
is also a minor but real stand-alone improvement. By only computing the
cell metrics for our primary font, we avoid a *lot* of wasted work when
loading fallback fonts, and also avoid that being a source of load
errors, which we don't yet handle gracefully[^3].
To validate this PR I've run the full set of font backend tests locally
on my Mac with no failures, and did a sanity check of running Ghostty
with both renderers and with CoreText and FreeType font backends and
then `cat`ing a file that requires fallback fonts to render, and
everything looks correct.
[^1]: #3029
[^2]: The alignment and resizing rules for the nerd font symbols are
defined in the patcher
[here](6d0b8ba05a/font-patcher (L866-L1151))
[^3]: #2991
This is achieved by rendering to an alpha-only context rather than a
normal single-channel context, and adjusting the brightness at which
CoreText thinks it's drawing the glyph, which affects how it applies
font smoothing (which is what `font-thicken` enables).
This is achieved by rendering to an alpha-only context rather than a
normal single-channel context, and adjusting the brightness at which
coretext thinks it's drawing the glyph, which affects how it applies
font smoothing (which is what `font-thicken` enables).
The renderer must track if the foreground, background, and cursor colors
are explicitly set by an OSC so that changes are not overridden when the
config file is reloaded.
Fixes: https://github.com/ghostty-org/ghostty/issues/2795
The renderer must track if the foreground, background, and cursor colors
are explicitly set by an OSC so that changes are not overridden when the
config file is reloaded.
Fixes#2332
Two bugs fixed to fix this behavior:
1. Our destination height didn't account for the top-left being
offscreen.
2. We were using the wrong height for the source rectangle. When a rows
param (r=) is specified, the image height and destination height are
at different scales. We were using the viewport scale for the offset
but it should be the image scale.
Fixes#2921
Our z-index handling was pretty much completely broken, hence I can't
think of a better initial commit message. We were splitting the
placements at the wrong points and just generally putting images in the
wrong z-index. I'm shocked this didn't come up earlier.
There is no reason to and I do not know where this assumption came from.
It's very possible for a colored glyph to (intentionally!) exceed the
cell bounds, and we shouldn't be stopping this...
This sets the macOS QoS class of the renderer thread. Apple
recommends[1] that all threads should have a QoS class set, and there
are many benefits[2] to that, mainly around power management moreso than
performance I'd expect.
In this commit, I start by setting the QoS class of the renderer thread.
By default, the renderer thread is set to user interactive, because it
is a UI thread after all. But under some conditions we downgrade:
- If the surface is not visible at all (i.e. another window is fully
covering it or its minimized), we set the QoS class to utility. This
is lower than the default, previous QoS and should help macOS
unschedule the workload or move it to a different core.
- If the surface is visible but not focused, we set the QoS class to
user initiated. This is lower than user interactive but higher than
default. The renderer should remain responsive but not consume as
much time as it would if it was user interactive.
I'm unable to see any noticable difference in anything from these
changes. Unfortunately it doesn't seem like Apple provides good tools to
play around with this.
We should continue to apply QoS classes to our other threads on macOS.
[1]: https://developer.apple.com/documentation/apple-silicon/tuning-your-code-s-performance-for-apple-silicon?preferredLanguage=occl
[2]: https://blog.xoria.org/macos-tips-threading/
This enables the compile options and Xcode configuration so that logging
in Metal shaders shows up in our Xcode debug console. This doesn't add
any log messages, but makes it so that when we iterate on the shaders in
the future, we can add and see logs to help us out.
This is more correct: a pagelist is a linked list of nodes, not pages.
The nodes themselves contain pages but we were previously calling the
nodes "pages" which was confusing, especially as I plan some future
changes to the way pages are stored.
Some Intel MacBook Pro laptops have both an integrated and discrete
GPU and support automatically switching between them. The system
uses the integrated GPU by default, but the default Metal device on
those systems is the discrete GPU. This means that Metal‐using
applications activate it by default, presumably as the intended
audience is high‐performance graphics applications.
This is unfortunate for productivity applications like terminals,
however, as the discrete GPU decreases battery life and worsens the
thermal throttling problems these machines have always had. Prefer
to use an integrated GPU when present and not using an external GPU.
The behaviour should be unchanged on Apple Silicon, as the platform
only supports one GPU. I have confirmed that the resulting app runs,
works, and doesn’t activate the AMD GPU on my MacBook Pro, but have
not done any measurements of the resulting performance impact. If
it is considered sufficiently noticeable, a GPU preference setting
could be added.
See <https://github.com/zed-industries/zed/issues/5124>,
<https://github.com/zed-industries/zed/pull/13685>,
<https://github.com/zed-industries/zed/pull/14738>, and
<https://github.com/zed-industries/zed/pull/14744> for discussion,
measurements, and changes relating to this issue in the Zed
project. The logic implemented here reflects what Zed ended up
settling on.
The [Metal documentation] recommends using
`MTLCopyAllDevicesWithObserver` to receive notifications of when
the list of available GPUs changes, such as when [external GPUs
are connected or disconnected]. I didn’t bother implementing that
because it seemed like a lot of fussy work to deal with migrating
everything to a new GPU on the fly just for a niche use case on a
legacy platform. Zed doesn’t implement it and I haven’t heard
about anyone complaining that their computer caught fire when they
unplugged an external GPU, so hopefully it’s fine.
[Metal documentation]: https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/multi-gpu_systems/finding_multiple_gpus_on_an_intel-based_mac
[external GPUs are connected or disconnected]: https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/multi-gpu_systems/handling_external_gpu_additions_and_removalsCloses: #2572