AllenDang / giu

Cross platform rapid GUI framework for golang based on Dear ImGui.
MIT License
2.19k stars 129 forks source link

[bug] SIGSEGV: Segment Violation with Progress Bar #819

Open xEricL opened 2 weeks ago

xEricL commented 2 weeks ago

What happend?

This code worked in v0.7. Updating to v0.8.1 causes a segment violation at runtime.

SIGSEGV: segmentation violation
PC=0x7fdd72531b29 m=13 sigcode=1 addr=0x998
signal arrived during cgo execution

goroutine 5 gp=0xc000007c00 m=13 mp=0xc000501808 [syscall]:
runtime.cgocall(0x74a7e0, 0xc000070590)
        runtime/cgocall.go:157 +0x4b fp=0xc000070568 sp=0xc000070530 pc=0x40ed0b
github.com/AllenDang/cimgui-go._Cfunc_igDeleteTexture(0x25)
        _cgo_gotypes.go:23190 +0x3f fp=0xc000070590 sp=0xc000070568 pc=0x6eb8ff
github.com/AllenDang/cimgui-go.(*GLFWBackend).DeleteTexture.func1({0x7fdd71a9c548?})
        github.com/AllenDang/cimgui-go@v0.0.0-20240611070805-39b5aa2438db/glfw_backend.go:376 +0x34 fp=0xc0000705c8 sp=0xc000070590 pc=0x6f2bf4
github.com/AllenDang/cimgui-go.(*GLFWBackend).DeleteTexture(0x10?, {0xc000501808?})
        github.com/AllenDang/cimgui-go@v0.0.0-20240611070805-39b5aa2438db/glfw_backend.go:376 +0x16 fp=0xc0000705e0 sp=0xc0000705c8 pc=0x6f2b96
github.com/AllenDang/cimgui-go.(*Texture).release(0x0?)
        github.com/AllenDang/cimgui-go@v0.0.0-20240611070805-39b5aa2438db/texture.go:36 +0x28 fp=0xc000070600 sp=0xc0000705e0 pc=0x6eac28
runtime.call16(0x0, 0xe17808, 0xc000124010, 0x10, 0x10, 0x10, 0xc000070690)
        runtime/asm_amd64.s:770 +0x43 fp=0xc000070620 sp=0xc000070600 pc=0x4755a3
runtime.runfinq()
        runtime/mfinal.go:256 +0x3f1 fp=0xc0000707e0 sp=0xc000070620 pc=0x424331
runtime.goexit({})
        runtime/asm_amd64.s:1695 +0x1 fp=0xc0000707e8 sp=0xc0000707e0 pc=0x477061
created by runtime.createfing in goroutine 1
        runtime/mfinal.go:164 +0x3d

goroutine 1 gp=0xc0000061c0 m=nil [select, locked to thread]:
runtime.gopark(0xc000551e50?, 0x2?, 0xb8?, 0x9?, 0xc000551e44?)
        runtime/proc.go:402 +0xce fp=0xc000551cf0 sp=0xc000551cd0 pc=0x4450ce
runtime.selectgo(0xc000551e50, 0xc000551e40, 0x18?, 0x0, 0xc000384030?, 0x1)
        runtime/select.go:327 +0x725 fp=0xc000551e10 sp=0xc000551cf0 pc=0x456705
github.com/faiface/mainthread.Run(0xc000384030)
        github.com/faiface/mainthread@v0.0.0-20171120011319-8b78f0a41ae3/mainthread.go:42 +0x10d fp=0xc000551e88 sp=0xc000551e10 pc=0x6ff5ad
github.com/AllenDang/giu.mainthreadCallPlatform(...)
        github.com/AllenDang/giu@v0.8.1/mainthread_all.go:9
github.com/AllenDang/giu.(*MasterWindow).Run(...)
        github.com/AllenDang/giu@v0.8.1/MasterWindow.go:236
github.com/alpine-client/pinnacle/ui.Render()
        github.com/alpine-client/pinnacle/ui/ui.go:112 +0x67 fp=0xc000551eb0 sp=0xc000551e88 pc=0x707d67
main.Run()
        github.com/alpine-client/pinnacle/main.go:50 +0xf0 fp=0xc000551f10 sp=0xc000551eb0 pc=0x7120b0
main.main()
        github.com/alpine-client/pinnacle/main.go:28 +0xb2 fp=0xc000551f50 sp=0xc000551f10 pc=0x711f92
runtime.main()
        runtime/proc.go:271 +0x29d fp=0xc000551fe0 sp=0xc000551f50 pc=0x444c9d
runtime.goexit({})
        runtime/asm_amd64.s:1695 +0x1 fp=0xc000551fe8 sp=0xc000551fe0 pc=0x477061

goroutine 2 gp=0xc000006c40 m=nil [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        runtime/proc.go:402 +0xce fp=0xc000070fa8 sp=0xc000070f88 pc=0x4450ce
runtime.goparkunlock(...)
        runtime/proc.go:408
runtime.forcegchelper()
        runtime/proc.go:326 +0xb3 fp=0xc000070fe0 sp=0xc000070fa8 pc=0x444f53
runtime.goexit({})
        runtime/asm_amd64.s:1695 +0x1 fp=0xc000070fe8 sp=0xc000070fe0 pc=0x477061
created by runtime.init.6 in goroutine 1
        runtime/proc.go:314 +0x1a

goroutine 3 gp=0xc000007180 m=nil [runnable]:
runtime.goschedIfBusy()
        runtime/proc.go:365 +0x28 fp=0xc000071780 sp=0xc000071768 pc=0x444fe8
runtime.bgsweep(0xc00009a000)
        runtime/mgcsweep.go:302 +0x14f fp=0xc0000717c8 sp=0xc000071780 pc=0x43076f
runtime.gcenable.gowrap1()
        runtime/mgc.go:203 +0x25 fp=0xc0000717e0 sp=0xc0000717c8 pc=0x425005
runtime.goexit({})
        runtime/asm_amd64.s:1695 +0x1 fp=0xc0000717e8 sp=0xc0000717e0 pc=0x477061
created by runtime.gcenable in goroutine 1
        runtime/mgc.go:203 +0x66

Code example

main.go ```golang //go:embed assets/* var assets embed.FS var ( logo *image.RGBA window *giu.MasterWindow ) func main(){ window = giu.NewMasterWindow( "Updater", 377, 144, giu.MasterWindowFlagsFrameless|giu.MasterWindowFlagsNotResizable|giu.MasterWindowFlagsTransparent, ) logo, _ = loadImage(assets, "assets/logo.png") window.Run(defaultUI) } func defaultUI() { giu.SingleWindow().Layout( giu.Align(giu.AlignCenter).To( giu.Dummy(0, scaleDivider(6)), giu.ImageWithRgba(logo).Size(80, 80), giu.Dummy(0, scaleDivider(6)), giu.ProgressBar(ReadProgress()).Size(scaleValueX(377)*0.75, scaleValueY(5)), ), ) } func scaleDivider(value float32) float32 { _, yScale := giu.Context.Backend().ContentScale() if yScale > 1.0 { value *= 2 } return value * yScale } func scaleValueY(value int) float32 { _, yScale := giu.Context.Backend().ContentScale() return float32(value) * yScale } func scaleValueX(value int) float32 { xScale, _ := giu.Context.Backend().ContentScale() return float32(value) * xScale } func loadImage(assets embed.FS, path string) (*image.RGBA, error) { data, err := assets.ReadFile(path) if err != nil { return nil, err } img, err := png.Decode(bytes.NewReader(data)) if err != nil { return nil, err } return giu.ImageToRgba(img), nil } ```

You can also find the actual code I'm using this for here: https://github.com/alpine-client/pinnacle/blob/084fde184a816b2241c36bd4f37a9fced3f21dbf/ui/ui.go#L83

To Reproduce

  1. Run my demo
  2. will see the crash...

Version

(latest)

OS

Fedora 40

gucio321 commented 2 weeks ago

@xEricL, I can't reproduce

Here is the code I'm using

package main

import (
    "bytes"
    "embed"
    "image"
    "image/png"

    "github.com/AllenDang/giu"
)

//go:embed assets/*
var assets embed.FS

var (
    logo   *image.RGBA
    window *giu.MasterWindow
)

func main() {
    window = giu.NewMasterWindow(
        "Updater",
        377, 144,
        giu.MasterWindowFlagsFrameless|giu.MasterWindowFlagsNotResizable|giu.MasterWindowFlagsTransparent,
    )
    logo, _ = loadImage(assets, "assets/logo.png")
    window.Run(defaultUI)
}

func defaultUI() {
    giu.SingleWindow().Layout(
        giu.Align(giu.AlignCenter).To(
            giu.Dummy(0, scaleDivider(6)),
            giu.ImageWithRgba(logo).Size(80, 80),
            giu.Dummy(0, scaleDivider(6)),
            giu.ProgressBar(ReadProgress()).Size(scaleValueX(377)*0.75, scaleValueY(5)),
        ),
    )
}

func ReadProgress() float32 {
    return 0.5
}

func scaleDivider(value float32) float32 {
    _, yScale := giu.Context.Backend().ContentScale()
    if yScale > 1.0 {
        value *= 2
    }
    return value * yScale
}

func scaleValueY(value int) float32 {
    _, yScale := giu.Context.Backend().ContentScale()
    return float32(value) * yScale
}

func scaleValueX(value int) float32 {
    xScale, _ := giu.Context.Backend().ContentScale()
    return float32(value) * xScale
}

func loadImage(assets embed.FS, path string) (*image.RGBA, error) {
    data, err := assets.ReadFile(path)
    if err != nil {
        return nil, err
    }

    img, err := png.Decode(bytes.NewReader(data))
    if err != nil {
        return nil, err
    }

    return giu.ImageToRgba(img), nil
}

It works fine (assets/logo.png for me is examples/loadimage/gopher.png)

I've a suggestion for your project: make sure your style.go does not use AllenDang/imgui-go - you need AllenDang/cimgui-go

xEricL commented 1 week ago

I've a suggestion for your project: make sure your style.go does not use AllenDang/imgui-go - you need AllenDang/cimgui-go

@gucio321 I've made sure to update my style.go. I've pushed the updated code to a new branch https://github.com/alpine-client/pinnacle/tree/giu-v0.8

I didn't think styling was the issue because it still happens even if I removed SetupStyle and PopStyle from the defaultUI() function.

After further testing, I've discovered 2 cases where the SIGSEV does not happen (which should narrow down the issue):

  1. Removing giu.ImageWithRgba(logo) from defaultUI(). Specifically, this line: https://github.com/alpine-client/pinnacle/blob/92462557244d9d37c35cae535529d894a9e5b21d/ui/ui.go#L88
  2. Do 1 successful run (case 1), but then add back the giu.ImageWithRgba(logo) line and run the code again (skips most of the progress bar)

Let me further explain our program. It basically downloads and unzips Java 17, downloads a launcher.jar from our website, and then uses the Java binary to run launcher.jar. This only needs to happen once, so if the program detects that the Java runtime package and launcher.jar already exist on the system, it doesn't do it again. This means the progress bar goes much faster after 1 successful run. As a result, if you get the program to run successfully, you need to delete ~/.alpineclient/ before running it again or else SIGSEV won't happen again (I know, it's very strange).

Since you're unable to recreate it, please clone this branch, run make run, and the SIGSEV should happen. Once again, the program only interacts with ~/.alpineclient/, so remember to delete that after a successful run (and at the end when you're finished testing). I've also commented out the code that runs launcher.jar (so no other code will be executed on your system).

Please let me know if you're still unable to recreate it under these conditions. The issue is most likely related to giu.ImageWithRgba (case 1). Although I don't know why the SIGSEV doesn't happen after 1 successful run (case 2).

xEricL commented 1 week ago

Even if I comment out the progress bar and only load the image by itself, the SIGSEV occurs. It is almost certainly caused by giu.ImageWithRgba.

xEricL commented 1 week ago

Replacing giu.ImageWithRgba with giu.ImageWithURL fixes the issue. Not an ideal solution but will have to do in the meantime. This is probably a lower-level issue with cimgui-go.

gucio321 commented 6 days ago

Image with url is almost the same as image with rgba iirc So this night be a giu issue, let me investigate it

gucio321 commented 6 days ago

@xEricL I'm sorry but I'm still unable to reproduce:

https://github.com/user-attachments/assets/c7559080-d283-4381-9916-cd4ebad18022

xEricL commented 6 days ago

That is very interesting. I just tested it in a Debian 11 Virtual Machine and it compiled and ran no problem. Perhaps it's just an issue with Fedora 40 or maybe even just a problem with my system.

Thank you for the video and for taking the time to look into this.

gucio321 commented 5 days ago

@xEricL I'm on fedora 39. I can check on f40 laptop later.

You use wayland or xorg? And what gpu?

xEricL commented 5 days ago

You use wayland or xorg? And what gpu?

I'm on xorg. GPU is NVIDIA 2060S.

gucio321 commented 5 days ago

@xEricL I managed to reproduce on fedora 40 laptop with nvidia gpu. I have no idea is it hardware or os thing yet. Will try to debug this.

gucio321 commented 5 days ago

fedora 40 PC with no gpu - still works for me. This must be something about gpu (but why?) Generally ImageWithRGBa has something to do with texture loading and this is deffinitly gpu thing.

xEricL commented 5 days ago

It likely has something to do with NVIDIA drivers for Fedora 40.

Very weird that giu.ImageWithURL and giu.ImageWithFile work just fine. I decided to just go with giu.ImageWithFile. Perhaps it will resolve on it's own with a NVIDIA driver update in the future. Thank you again for taking the time to look into this for me.