mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
renderer/metal: extract out some image placement logic
This commit is contained in:
@ -124,6 +124,7 @@ images: ImageMap = .{},
|
|||||||
image_placements: ImagePlacementList = .{},
|
image_placements: ImagePlacementList = .{},
|
||||||
image_bg_end: u32 = 0,
|
image_bg_end: u32 = 0,
|
||||||
image_text_end: u32 = 0,
|
image_text_end: u32 = 0,
|
||||||
|
image_virtual: bool = false,
|
||||||
|
|
||||||
/// Metal state
|
/// Metal state
|
||||||
shaders: Shaders, // Compiled shaders
|
shaders: Shaders, // Compiled shaders
|
||||||
@ -927,7 +928,14 @@ pub fn updateFrame(
|
|||||||
// If we have Kitty graphics data, we enter a SLOW SLOW SLOW path.
|
// If we have Kitty graphics data, we enter a SLOW SLOW SLOW path.
|
||||||
// We only do this if the Kitty image state is dirty meaning only if
|
// We only do this if the Kitty image state is dirty meaning only if
|
||||||
// it changes.
|
// it changes.
|
||||||
if (state.terminal.screen.kitty_images.dirty) {
|
//
|
||||||
|
// If we have any virtual references, we must also rebuild our
|
||||||
|
// kitty state on every frame because any cell change can move
|
||||||
|
// an image.
|
||||||
|
// TODO(mitchellh): integrate with row dirty flags
|
||||||
|
if (state.terminal.screen.kitty_images.dirty or
|
||||||
|
self.image_virtual)
|
||||||
|
{
|
||||||
try self.prepKittyGraphics(state.terminal);
|
try self.prepKittyGraphics(state.terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1565,6 +1573,7 @@ fn prepKittyGraphics(
|
|||||||
// We always clear our previous placements no matter what because
|
// We always clear our previous placements no matter what because
|
||||||
// we rebuild them from scratch.
|
// we rebuild them from scratch.
|
||||||
self.image_placements.clearRetainingCapacity();
|
self.image_placements.clearRetainingCapacity();
|
||||||
|
self.image_virtual = false;
|
||||||
|
|
||||||
// Go through our known images and if there are any that are no longer
|
// Go through our known images and if there are any that are no longer
|
||||||
// in use then mark them to be freed.
|
// in use then mark them to be freed.
|
||||||
@ -1588,8 +1597,25 @@ fn prepKittyGraphics(
|
|||||||
// Go through the placements and ensure the image is loaded on the GPU.
|
// Go through the placements and ensure the image is loaded on the GPU.
|
||||||
var it = storage.placements.iterator();
|
var it = storage.placements.iterator();
|
||||||
while (it.next()) |kv| {
|
while (it.next()) |kv| {
|
||||||
// Find the image in storage
|
|
||||||
const p = kv.value_ptr;
|
const p = kv.value_ptr;
|
||||||
|
|
||||||
|
// Special logic based on location
|
||||||
|
switch (p.location) {
|
||||||
|
.pin => {},
|
||||||
|
.virtual => {
|
||||||
|
// We need to mark virtual placements on our renderer so that
|
||||||
|
// we know to rebuild in more scenarios since cell changes can
|
||||||
|
// now trigger placement changes.
|
||||||
|
self.image_virtual = true;
|
||||||
|
|
||||||
|
// We also continue out because virtual placements are
|
||||||
|
// only triggered by the unicode placeholder, not by the
|
||||||
|
// placement itself.
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the image for the placement
|
||||||
const image = storage.imageById(kv.key_ptr.image_id) orelse {
|
const image = storage.imageById(kv.key_ptr.image_id) orelse {
|
||||||
log.warn(
|
log.warn(
|
||||||
"missing image for placement, ignoring image_id={}",
|
"missing image for placement, ignoring image_id={}",
|
||||||
@ -1598,18 +1624,63 @@ fn prepKittyGraphics(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try self.prepKittyPlacement(t, &top, &bot, &image, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the placements by their Z value.
|
||||||
|
std.mem.sortUnstable(
|
||||||
|
mtl_image.Placement,
|
||||||
|
self.image_placements.items,
|
||||||
|
{},
|
||||||
|
struct {
|
||||||
|
fn lessThan(
|
||||||
|
ctx: void,
|
||||||
|
lhs: mtl_image.Placement,
|
||||||
|
rhs: mtl_image.Placement,
|
||||||
|
) bool {
|
||||||
|
_ = ctx;
|
||||||
|
return lhs.z < rhs.z or (lhs.z == rhs.z and lhs.image_id < rhs.image_id);
|
||||||
|
}
|
||||||
|
}.lessThan,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find our indices
|
||||||
|
self.image_bg_end = 0;
|
||||||
|
self.image_text_end = 0;
|
||||||
|
const bg_limit = std.math.minInt(i32) / 2;
|
||||||
|
for (self.image_placements.items, 0..) |p, i| {
|
||||||
|
if (self.image_bg_end == 0 and p.z >= bg_limit) {
|
||||||
|
self.image_bg_end = @intCast(i);
|
||||||
|
}
|
||||||
|
if (self.image_text_end == 0 and p.z >= 0) {
|
||||||
|
self.image_text_end = @intCast(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self.image_text_end == 0) {
|
||||||
|
self.image_text_end = @intCast(self.image_placements.items.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepKittyPlacement(
|
||||||
|
self: *Metal,
|
||||||
|
t: *terminal.Terminal,
|
||||||
|
top: *const terminal.Pin,
|
||||||
|
bot: *const terminal.Pin,
|
||||||
|
image: *const terminal.kitty.graphics.Image,
|
||||||
|
p: *const terminal.kitty.graphics.ImageStorage.Placement,
|
||||||
|
) !void {
|
||||||
// Get the rect for the placement. If this placement doesn't have
|
// Get the rect for the placement. If this placement doesn't have
|
||||||
// a rect then its virtual or something so skip it.
|
// a rect then its virtual or something so skip it.
|
||||||
const rect = p.rect(image, t) orelse continue;
|
const rect = p.rect(image.*, t) orelse return;
|
||||||
|
|
||||||
// If the selection isn't within our viewport then skip it.
|
// If the selection isn't within our viewport then skip it.
|
||||||
if (bot.before(rect.top_left)) continue;
|
if (bot.before(rect.top_left)) return;
|
||||||
if (rect.bottom_right.before(top)) continue;
|
if (rect.bottom_right.before(top.*)) return;
|
||||||
|
|
||||||
// If the top left is outside the viewport we need to calc an offset
|
// If the top left is outside the viewport we need to calc an offset
|
||||||
// so that we render (0, 0) with some offset for the texture.
|
// so that we render (0, 0) with some offset for the texture.
|
||||||
const offset_y: u32 = if (rect.top_left.before(top)) offset_y: {
|
const offset_y: u32 = if (rect.top_left.before(top.*)) offset_y: {
|
||||||
const vp_y = t.screen.pages.pointFromPin(.screen, top).?.screen.y;
|
const vp_y = t.screen.pages.pointFromPin(.screen, top.*).?.screen.y;
|
||||||
const img_y = t.screen.pages.pointFromPin(.screen, rect.top_left).?.screen.y;
|
const img_y = t.screen.pages.pointFromPin(.screen, rect.top_left).?.screen.y;
|
||||||
const offset_cells = vp_y - img_y;
|
const offset_cells = vp_y - img_y;
|
||||||
const offset_pixels = offset_cells * self.grid_metrics.cell_height;
|
const offset_pixels = offset_cells * self.grid_metrics.cell_height;
|
||||||
@ -1619,7 +1690,7 @@ fn prepKittyGraphics(
|
|||||||
// We need to prep this image for upload if it isn't in the cache OR
|
// We need to prep this image for upload if it isn't in the cache OR
|
||||||
// it is in the cache but the transmit time doesn't match meaning this
|
// it is in the cache but the transmit time doesn't match meaning this
|
||||||
// image is different.
|
// image is different.
|
||||||
const gop = try self.images.getOrPut(self.alloc, kv.key_ptr.image_id);
|
const gop = try self.images.getOrPut(self.alloc, image.id);
|
||||||
if (!gop.found_existing or
|
if (!gop.found_existing or
|
||||||
gop.value_ptr.transmit_time.order(image.transmit_time) != .eq)
|
gop.value_ptr.transmit_time.order(image.transmit_time) != .eq)
|
||||||
{
|
{
|
||||||
@ -1681,7 +1752,7 @@ fn prepKittyGraphics(
|
|||||||
// Accumulate the placement
|
// Accumulate the placement
|
||||||
if (image.width > 0 and image.height > 0) {
|
if (image.width > 0 and image.height > 0) {
|
||||||
try self.image_placements.append(self.alloc, .{
|
try self.image_placements.append(self.alloc, .{
|
||||||
.image_id = kv.key_ptr.image_id,
|
.image_id = image.id,
|
||||||
.x = @intCast(rect.top_left.x),
|
.x = @intCast(rect.top_left.x),
|
||||||
.y = @intCast(viewport.viewport.y),
|
.y = @intCast(viewport.viewport.y),
|
||||||
.z = p.z,
|
.z = p.z,
|
||||||
@ -1695,40 +1766,6 @@ fn prepKittyGraphics(
|
|||||||
.source_height = source_height,
|
.source_height = source_height,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the placements by their Z value.
|
|
||||||
std.mem.sortUnstable(
|
|
||||||
mtl_image.Placement,
|
|
||||||
self.image_placements.items,
|
|
||||||
{},
|
|
||||||
struct {
|
|
||||||
fn lessThan(
|
|
||||||
ctx: void,
|
|
||||||
lhs: mtl_image.Placement,
|
|
||||||
rhs: mtl_image.Placement,
|
|
||||||
) bool {
|
|
||||||
_ = ctx;
|
|
||||||
return lhs.z < rhs.z or (lhs.z == rhs.z and lhs.image_id < rhs.image_id);
|
|
||||||
}
|
|
||||||
}.lessThan,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find our indices
|
|
||||||
self.image_bg_end = 0;
|
|
||||||
self.image_text_end = 0;
|
|
||||||
const bg_limit = std.math.minInt(i32) / 2;
|
|
||||||
for (self.image_placements.items, 0..) |p, i| {
|
|
||||||
if (self.image_bg_end == 0 and p.z >= bg_limit) {
|
|
||||||
self.image_bg_end = @intCast(i);
|
|
||||||
}
|
|
||||||
if (self.image_text_end == 0 and p.z >= 0) {
|
|
||||||
self.image_text_end = @intCast(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (self.image_text_end == 0) {
|
|
||||||
self.image_text_end = @intCast(self.image_placements.items.len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the configuration.
|
/// Update the configuration.
|
||||||
|
Reference in New Issue
Block a user