Skip to content

Commit

Permalink
Large memory reductions when working on multiple images simultaneously
Browse files Browse the repository at this point in the history
When loading a new image, PD will now automatically suspend inactive images to compressed buffers.  This is a very fast operation (lz4 is used) and for "normal" image sizes - e.g. < 20 megapixels - any delays caused by the suspend process are basically unnoticeable.  On larger images there may be a brief stutter when switching rapidly between loaded images, but the given the huge memory reductions, I think this is a worthwhile modification.

In the future it may be nice to have an option to go even further and suspend-to-disk if memory is constrained, but for now, compressed in-memory buffers provide a fast, simple way to cut memory usage when working with multiple side-by-side images.  (I may look at extending this auto-suspend feature to other places, for example prior to initializing demanding tools.)
  • Loading branch information
tannerhelland committed Oct 24, 2021
1 parent b643abb commit dbac890
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Classes/pdDIB.cls
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ Private Sub UnsuspendDIB()
'A lot of different strategies are possible here, but for now, let's keep the suspend
' buffer intact for small images. These are likely to suspend/unsuspend many times
' as part of UI rendering, and the cost of keeping the suspend buffer intact is less
' than the cost of constant small re-allocations
' than the cost of constant small re-allocations.
If (m_SizeSuspended > 16384) Then Erase m_SuspensionBuffer

m_SizeSuspended = 0
Expand Down
31 changes: 27 additions & 4 deletions Classes/pdImage.cls
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ Attribute VB_Exposed = False
'PhotoDemon Image class
'Copyright 2006-2021 by Tanner Helland
'Created: sometime 2006
'Last updated: 11/January/21
'Last update: harden against potential errors when rapidly creating/deleting layers on-the-fly
'Last updated: 24/October/21
'Last update: new Suspend option, for reducing memory usage when a pdImage isn't active
'
'The pdImage class is used to store information on each image loaded by the user.
' One instance of this class exists for each loaded image.
Expand Down Expand Up @@ -739,6 +739,22 @@ Friend Sub ReverseLayerOrder()

End Sub

'Non-destructively suspend as many image resources as we can to either disk or memory.
Friend Sub SuspendImage()

'Start by suspending all layers
Dim i As Long
For i = 0 To UBound(imgLayers)
If (Not imgLayers(i) Is Nothing) Then imgLayers(i).SuspendLayer True
Next i

'Suspend any other internal DIBs
If (Not m_ImageThumbnail Is Nothing) Then m_ImageThumbnail.SuspendDIB cf_Lz4, False
If (Not CompositeBuffer Is Nothing) Then CompositeBuffer.SuspendDIB cf_Lz4, False
If (Not CanvasBuffer Is Nothing) Then CanvasBuffer.SuspendDIB cf_Lz4, False

End Sub

'Used by various layer movement functions. Given two layer indices, swap them.
Friend Sub SwapTwoLayers(ByVal srcLayerIndex_1 As Long, ByVal srcLayerIndex_2 As Long)

Expand Down Expand Up @@ -1166,14 +1182,21 @@ End Function
'When the attached image object is deactivated (e.g. the user leaves it loaded, but switches to a
' different image), you can call this function to free up some internal memory caches and other objects.
' This can free a non-trivial amount of resources when system memory is tight.
Friend Sub DeactivateImage()
'
'For even more resource reduction, ask for a full layer suspension too - this will ask all child layers
' to suspend their DIBs to compressed buffers.
Friend Sub DeactivateImage(Optional ByVal suspendLayersToo As Boolean = True)

'Scratch layers aren't needed unless the image is actively being edited
Set ScratchLayer = Nothing

'Some internal selection caches are free-able
If (Not MainSelection Is Nothing) Then MainSelection.FreeNonEssentialResources


'Layer suspension is the biggest gain, but it also has a perf penalty so the caller may
' choose to avoid this.
If suspendLayersToo Then Me.SuspendImage

End Sub

'When the attached image object is closed, we can deactivate a ton of items to save on resources.
Expand Down
20 changes: 20 additions & 0 deletions Classes/pdLayer.cls
Original file line number Diff line number Diff line change
Expand Up @@ -2297,6 +2297,26 @@ Friend Sub ResetLayerParameters()

End Sub

'Non-destructively suspend as many layer resources as we can to either disk or memory.
Friend Sub SuspendLayer(Optional ByVal suspendViewportToo As Boolean = False)

'Layer DIB is the most obvious target for memory reduction
If (Not layerDIB Is Nothing) Then layerDIB.SuspendDIB cf_Lz4, False

'Thumbnail is an easy target too (since it's small, suspending is roughly instantaneous)
If (Not m_LayerThumbnail Is Nothing) Then m_LayerThumbnail.SuspendDIB cf_Lz4, False

'Finally, lodDIBs can be suspended too, but be careful with these because there are meaningful
' perf implications for viewport rendering if these have to be swapped back in frequently.
If suspendViewportToo Then
Dim i As Long
For i = LBound(lodDIB) To UBound(lodDIB)
If (Not lodDIB(i) Is Nothing) Then lodDIB(i).SuspendDIB cf_Lz4, False
Next i
End If

End Sub

'If something needs to access the layer DIB directly (like PD's central compositor), this function should first be called for
' any vector layers. If the DIB is already synched, there is no penalty to calling this function, but if it is *not* synched,
' this will guarantee the DIB is ready for access.
Expand Down
3 changes: 2 additions & 1 deletion Modules/CanvasManager.bas
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,8 @@ Public Sub ActivatePDImage(ByVal imageID As Long, Optional ByRef reasonForActiva
If activeImageChanging Then

'Release some temporary resources on the old image, if we can
PDImages.GetActiveImage.DeactivateImage
PDImages.GetActiveImage.DeactivateImage True
UIImages.FreeSharedCompressBuffer

'Update the current form variable
PDImages.SetActiveImageID imageID
Expand Down
10 changes: 10 additions & 0 deletions Modules/PDImages.bas
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,13 @@ End Sub
Public Sub SetActiveImageID(ByVal newID As Long)
m_CurrentImageID = newID
End Sub

'Given an ID in the pdImages collection, suspend all layers (and other high-memory objects) inside that image.
' This is a non-destructive process. Necessary resources will be automatically unsuspended when those resources
' are requested. Note, however, that like any large-scale data transfer, there is a perf cost associated with
' doing this. Typically, suspending an image should be saved for things like switching to a new active image
' (and the old image gets suspended), or opening a high-resource-usage tool and the background image won't be
' accessible for awhile.
Public Sub SuspendImage(ByVal imgID As Long)
'if pdimages.IsImageNonNull(imgid) then
End Sub
8 changes: 8 additions & 0 deletions Modules/UIImages.bas
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ Public Function AddImage(ByRef srcDIB As pdDIB, ByRef uniqueImageName As String)

End Function

'Free the shared compression buffer for UI images. This carries a ripple effect for PD's internal
' suspend-to-memory operations, so do this only if the memory savings are large.
Public Sub FreeSharedCompressBuffer()
PDDebug.LogAction "Freeing shared memory buffer (size " & Files.GetFormattedFileSize(m_CompressBufferSize) & ")"
Erase m_TempCompressBuffer
m_CompressBufferSize = 0
End Sub

'Return a standalone DIB of a given sprite. Do *not* use this more than absolutely necessary,
' as it is expensive to initialize sprites (and it sort of defeats the purpose of using a
' sprite sheet in the first place!)
Expand Down
2 changes: 1 addition & 1 deletion PhotoDemon.vbp
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ Description="PhotoDemon Photo Editor"
CompatibleMode="0"
MajorVer=8
MinorVer=9
RevisionVer=959
RevisionVer=962
AutoIncrementVer=1
ServerSupportFiles=0
VersionComments="Copyright 2000-2021 Tanner Helland - photodemon.org"
Expand Down

0 comments on commit dbac890

Please sign in to comment.