perf(ui): use efficient group caching instead of a compositing rect

Seems to be the same speed and it's less complex.
This commit is contained in:
psychedelicious 2024-04-20 13:37:21 +10:00
parent 39d036bb37
commit 604bf4e9ec
2 changed files with 14 additions and 20 deletions

View File

@ -355,7 +355,6 @@ const getRPLayerId = (layerId: string) => `rp_layer_${layerId}`;
const getRPLayerLineId = (layerId: string, lineId: string) => `${layerId}.line_${lineId}`; const getRPLayerLineId = (layerId: string, lineId: string) => `${layerId}.line_${lineId}`;
export const getRPLayerObjectGroupId = (layerId: string, groupId: string) => `${layerId}.objectGroup_${groupId}`; export const getRPLayerObjectGroupId = (layerId: string, groupId: string) => `${layerId}.objectGroup_${groupId}`;
export const getPRLayerBboxId = (layerId: string) => `${layerId}.bbox`; export const getPRLayerBboxId = (layerId: string) => `${layerId}.bbox`;
export const getRPLayerTransparencyRectId = (layerId: string) => `${layerId}.transparency_rect`;
export const regionalPromptsPersistConfig: PersistConfig<RegionalPromptsState> = { export const regionalPromptsPersistConfig: PersistConfig<RegionalPromptsState> = {
name: regionalPromptsSlice.name, name: regionalPromptsSlice.name,

View File

@ -9,7 +9,6 @@ import {
BRUSH_PREVIEW_LAYER_ID, BRUSH_PREVIEW_LAYER_ID,
getPRLayerBboxId, getPRLayerBboxId,
getRPLayerObjectGroupId, getRPLayerObjectGroupId,
getRPLayerTransparencyRectId,
REGIONAL_PROMPT_LAYER_BBOX_NAME, REGIONAL_PROMPT_LAYER_BBOX_NAME,
REGIONAL_PROMPT_LAYER_LINE_NAME, REGIONAL_PROMPT_LAYER_LINE_NAME,
REGIONAL_PROMPT_LAYER_NAME, REGIONAL_PROMPT_LAYER_NAME,
@ -192,16 +191,6 @@ const renderRPLayer = (
}); });
konvaLayer.add(konvaObjectGroup); konvaLayer.add(konvaObjectGroup);
// To achieve performant transparency, we use the `source-in` blending mode on a rect that covers the entire layer.
// The brush strokes group functions as a mask for this rect, which has the layer's fill and opacity. The brush
// strokes' color doesn't matter - the only requirement is that they are not transparent.
const transparencyRect = new Konva.Rect({
id: getRPLayerTransparencyRectId(rpLayer.id),
globalCompositeOperation: 'source-in',
listening: false,
});
konvaLayer.add(transparencyRect);
stage.add(konvaLayer); stage.add(konvaLayer);
// When a layer is added, it ends up on top of the brush preview - we need to move the preview back to the top. // When a layer is added, it ends up on top of the brush preview - we need to move the preview back to the top.
@ -223,14 +212,20 @@ const renderRPLayer = (
const konvaObjectGroup = konvaLayer.findOne<Konva.Group>(`.${REGIONAL_PROMPT_LAYER_OBJECT_GROUP_NAME}`); const konvaObjectGroup = konvaLayer.findOne<Konva.Group>(`.${REGIONAL_PROMPT_LAYER_OBJECT_GROUP_NAME}`);
assert(konvaObjectGroup, `Object group not found for layer ${rpLayer.id}`); assert(konvaObjectGroup, `Object group not found for layer ${rpLayer.id}`);
const transparencyRect = konvaLayer.findOne<Konva.Rect>(`#${getRPLayerTransparencyRectId(rpLayer.id)}`); // We use caching to handle "global" layer opacity, but caching is expensive and we should only do it when required.
assert(transparencyRect, `Transparency rect not found for layer ${rpLayer.id}`); let groupNeedsCache = false;
if (konvaObjectGroup.opacity() !== layerOpacity) {
konvaObjectGroup.opacity(layerOpacity);
groupNeedsCache = true;
}
// Remove deleted objects // Remove deleted objects
const objectIds = rpLayer.objects.map(mapId); const objectIds = rpLayer.objects.map(mapId);
for (const objectNode of konvaLayer.find(`.${REGIONAL_PROMPT_LAYER_LINE_NAME}`)) { for (const objectNode of konvaLayer.find(`.${REGIONAL_PROMPT_LAYER_LINE_NAME}`)) {
if (!objectIds.includes(objectNode.id())) { if (!objectIds.includes(objectNode.id())) {
objectNode.destroy(); objectNode.destroy();
groupNeedsCache = true;
} }
} }
@ -262,23 +257,23 @@ const renderRPLayer = (
// Only update the points if they have changed. The point values are never mutated, they are only added to the array. // Only update the points if they have changed. The point values are never mutated, they are only added to the array.
if (konvaObject.points().length !== reduxObject.points.length) { if (konvaObject.points().length !== reduxObject.points.length) {
konvaObject.points(reduxObject.points); konvaObject.points(reduxObject.points);
groupNeedsCache = true;
} }
// Only update the color if it has changed. // Only update the color if it has changed.
if (konvaObject.stroke() !== color) { if (konvaObject.stroke() !== color) {
konvaObject.stroke(color); konvaObject.stroke(color);
groupNeedsCache = true;
} }
// Only update layer visibility if it has changed. // Only update layer visibility if it has changed.
if (konvaObject.visible() !== rpLayer.isVisible) { if (konvaObject.visible() !== rpLayer.isVisible) {
konvaObject.visible(rpLayer.isVisible); konvaObject.visible(rpLayer.isVisible);
groupNeedsCache = true;
} }
} }
// Set the layer opacity - must happen after all objects are added to the layer so the rect is the right size if (groupNeedsCache) {
transparencyRect.setAttrs({ konvaObjectGroup.cache();
...konvaLayer.getClientRect({ skipTransform: true }), }
fill: color,
opacity: layerOpacity,
});
}; };
/** /**