From 9b89462620d6c2f3a10ed40b5ba679ad029afab6 Mon Sep 17 00:00:00 2001 From: "Colin J. Brigato" Date: Fri, 4 Oct 2024 13:53:30 +0200 Subject: [PATCH 1/2] custom texture binding for Reflective to avoid double free, and race condition fix in appending draw commands for paint example --- ReflectiveBoundTexture.go | 13 ++++++++++--- examples/paint/canvas.go | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ReflectiveBoundTexture.go b/ReflectiveBoundTexture.go index a4750f3a..1c72841b 100644 --- a/ReflectiveBoundTexture.go +++ b/ReflectiveBoundTexture.go @@ -7,6 +7,7 @@ import ( "image/color" "sync" + "github.com/AllenDang/cimgui-go/backend" "github.com/AllenDang/cimgui-go/imgui" ) @@ -193,10 +194,16 @@ func (i *ReflectiveBoundTexture) unbind() { } // bind creates a new texture from the RGBA surface and assigns it to the ReflectiveBoundTexture. +// note it bypasses normal texture management up to cimgui-go to avoid double free from finalizers. func (i *ReflectiveBoundTexture) bind() { - NewTextureFromRgba(i.Surface, func(tex *Texture) { - i.tex = tex - }) + img := ImageToRgba(i.Surface) + i.tex = &Texture{ + &backend.Texture{ + ID: backend.TextureManager(Context.backend).CreateTextureRgba(img, img.Bounds().Dx(), img.Bounds().Dy()), + Width: img.Bounds().Dx(), + Height: img.Bounds().Dy(), + }, + } } // GetSurfaceWidth returns the width of the RGBA surface. diff --git a/examples/paint/canvas.go b/examples/paint/canvas.go index beb32401..2651e3d2 100644 --- a/examples/paint/canvas.go +++ b/examples/paint/canvas.go @@ -4,6 +4,7 @@ import ( "fmt" "image" "image/color" + "sync" "github.com/AllenDang/cimgui-go/imgui" @@ -57,11 +58,13 @@ type Canvas struct { // inited is a boolean flag indicating whether the canvas has been initialized. inited bool + + appendMu sync.Mutex } -// GetDrawCommands returns a slice of drawCommand starting from the specified index. +// getDrawCommands returns a slice of drawCommand starting from the specified index. // It allows retrieval of draw commands that have been added since a given point in time. -func (c *Canvas) GetDrawCommands(sinceIndex int) []DrawCommand { +func (c *Canvas) getDrawCommands(sinceIndex int) []DrawCommand { return c.DrawCommands[sinceIndex:] } @@ -79,6 +82,8 @@ func (c *Canvas) PushImageToBackend(commit bool) error { // AppendDrawCommands adds a slice of drawCommand to the canvas's existing draw commands. // It appends the provided commands to the DrawCommands slice. func (c *Canvas) AppendDrawCommands(cmds *[]DrawCommand) { + c.appendMu.Lock() + defer c.appendMu.Unlock() c.DrawCommands = append(c.DrawCommands, *cmds...) } @@ -104,6 +109,8 @@ func (c *Canvas) Compute() { return } + c.appendMu.Lock() + // Return if there are no draw commands to process if len(c.DrawCommands) < 1 { return @@ -115,7 +122,9 @@ func (c *Canvas) Compute() { } // Get the new draw commands that need to be processed - draws := c.GetDrawCommands(c.LastComputedLen) + draws := c.getDrawCommands(c.LastComputedLen) + c.appendMu.Unlock() + for _, r := range draws { switch r.Tool { case 0: From 7a2287175f06060a9eda24c34f4bfd1e2e00d78e Mon Sep 17 00:00:00 2001 From: "Colin J. Brigato" Date: Fri, 4 Oct 2024 14:06:36 +0200 Subject: [PATCH 2/2] fix unlock --- examples/paint/canvas.go | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/examples/paint/canvas.go b/examples/paint/canvas.go index 2651e3d2..4416a08d 100644 --- a/examples/paint/canvas.go +++ b/examples/paint/canvas.go @@ -109,22 +109,29 @@ func (c *Canvas) Compute() { return } - c.appendMu.Lock() + var draws []DrawCommand - // Return if there are no draw commands to process - if len(c.DrawCommands) < 1 { - return - } + if func() bool { + c.appendMu.Lock() + defer c.appendMu.Unlock() + + // Return if there are no draw commands to process + if len(c.DrawCommands) < 1 { + return true + } + + // Return if all draw commands have already been processed + if len(c.DrawCommands) <= c.LastComputedLen { + return true + } - // Return if all draw commands have already been processed - if len(c.DrawCommands) <= c.LastComputedLen { + // Get the new draw commands that need to be processed + draws = c.getDrawCommands(c.LastComputedLen) + return false + }() { return } - // Get the new draw commands that need to be processed - draws := c.getDrawCommands(c.LastComputedLen) - c.appendMu.Unlock() - for _, r := range draws { switch r.Tool { case 0: