mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
Reduce history system's excessive memory usage from storing copies of images each step (#845)
* Limit saved undo history * Do not commit path every mouse move event * Reference count images in history system instead of cloning the image data * Increase history storage step limit to 100 Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -77,5 +77,6 @@ pub const DEFAULT_FONT_STYLE: &str = "Normal (400)";
|
||||
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.15"; // Remember to save a simple document and replace the test file `graphite-test-document.graphite`
|
||||
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
||||
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
||||
pub const MAX_UNDO_HISTORY_LEN: usize = 100; // TODO: Add this to user preferences
|
||||
|
||||
pub const VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR: f32 = 1.05;
|
||||
|
||||
@@ -15,8 +15,8 @@ pub struct FrontendDocumentDetails {
|
||||
pub struct FrontendImageData {
|
||||
pub path: Vec<LayerId>,
|
||||
pub mime: String,
|
||||
#[serde(rename = "imageData")]
|
||||
pub image_data: Vec<u8>,
|
||||
#[serde(skip)]
|
||||
pub image_data: std::rc::Rc<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
@@ -49,9 +49,9 @@ pub struct DocumentMessageHandler {
|
||||
pub overlays_visible: bool,
|
||||
|
||||
#[serde(skip)]
|
||||
pub document_undo_history: Vec<DocumentSave>,
|
||||
pub document_undo_history: VecDeque<DocumentSave>,
|
||||
#[serde(skip)]
|
||||
pub document_redo_history: Vec<DocumentSave>,
|
||||
pub document_redo_history: VecDeque<DocumentSave>,
|
||||
|
||||
#[serde(with = "vectorize_layer_metadata")]
|
||||
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
||||
@@ -80,8 +80,8 @@ impl Default for DocumentMessageHandler {
|
||||
snapping_enabled: true,
|
||||
overlays_visible: true,
|
||||
|
||||
document_undo_history: Vec::new(),
|
||||
document_redo_history: Vec::new(),
|
||||
document_undo_history: VecDeque::new(),
|
||||
document_redo_history: VecDeque::new(),
|
||||
|
||||
layer_metadata: vec![(vec![], LayerMetadata::new(true))].into_iter().collect(),
|
||||
layer_range_selection_reference: Vec::new(),
|
||||
@@ -508,7 +508,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||
);
|
||||
}
|
||||
MoveSelectedManipulatorPoints { layer_path, delta } => {
|
||||
self.backup(responses);
|
||||
if let Ok(_layer) = self.graphene_document.layer(&layer_path) {
|
||||
responses.push_back(DocumentOperation::MoveSelectedManipulatorPoints { layer_path, delta }.into());
|
||||
}
|
||||
@@ -541,6 +540,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
let image_data = std::rc::Rc::new(image_data);
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateImageData {
|
||||
document_id,
|
||||
@@ -1322,7 +1322,10 @@ impl DocumentMessageHandler {
|
||||
|
||||
pub fn backup(&mut self, responses: &mut VecDeque<Message>) {
|
||||
self.document_redo_history.clear();
|
||||
self.document_undo_history.push((self.graphene_document.clone(), self.layer_metadata.clone()));
|
||||
self.document_undo_history.push_back((self.graphene_document.clone(), self.layer_metadata.clone()));
|
||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||
self.document_undo_history.pop_front();
|
||||
}
|
||||
|
||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||
responses.push_back(PortfolioMessage::UpdateOpenDocumentsList.into());
|
||||
@@ -1340,7 +1343,7 @@ impl DocumentMessageHandler {
|
||||
|
||||
let selected_paths: Vec<Vec<LayerId>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
||||
|
||||
match self.document_undo_history.pop() {
|
||||
match self.document_undo_history.pop_back() {
|
||||
Some((document, layer_metadata)) => {
|
||||
// Update the currently displayed layer on the Properties panel if the selection changes after an undo action
|
||||
// Also appropriately update the Properties panel if an undo action results in a layer being deleted
|
||||
@@ -1352,7 +1355,10 @@ impl DocumentMessageHandler {
|
||||
|
||||
let document = std::mem::replace(&mut self.graphene_document, document);
|
||||
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
||||
self.document_redo_history.push((document, layer_metadata));
|
||||
self.document_redo_history.push_back((document, layer_metadata));
|
||||
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||
self.document_redo_history.pop_front();
|
||||
}
|
||||
|
||||
for layer in self.layer_metadata.keys() {
|
||||
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into())
|
||||
@@ -1370,7 +1376,7 @@ impl DocumentMessageHandler {
|
||||
|
||||
let selected_paths: Vec<Vec<LayerId>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
||||
|
||||
match self.document_redo_history.pop() {
|
||||
match self.document_redo_history.pop_back() {
|
||||
Some((document, layer_metadata)) => {
|
||||
// Update currently displayed layer on property panel if selection changes after redo action
|
||||
// Also appropriately update property panel if redo action results in a layer being added
|
||||
@@ -1382,7 +1388,10 @@ impl DocumentMessageHandler {
|
||||
|
||||
let document = std::mem::replace(&mut self.graphene_document, document);
|
||||
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
||||
self.document_undo_history.push((document, layer_metadata));
|
||||
self.document_undo_history.push_back((document, layer_metadata));
|
||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||
self.document_undo_history.pop_front();
|
||||
}
|
||||
|
||||
for layer in self.layer_metadata.keys() {
|
||||
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into())
|
||||
@@ -1398,6 +1407,7 @@ impl DocumentMessageHandler {
|
||||
// We can use the last state of the document to serve as the identifier to compare against
|
||||
// This is useful since when the document is empty the identifier will be 0
|
||||
self.document_undo_history
|
||||
.iter()
|
||||
.last()
|
||||
.map(|(graphene_document, _)| graphene_document.current_state_identifier())
|
||||
.unwrap_or(0)
|
||||
|
||||
@@ -476,6 +476,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
||||
.into(),
|
||||
);
|
||||
let mime = "image/bmp".to_string();
|
||||
let image_data = std::rc::Rc::new(image_data);
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateImageData {
|
||||
document_id,
|
||||
|
||||
Reference in New Issue
Block a user