Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak #64

Closed
alenskorobogatova opened this issue Oct 15, 2021 · 15 comments · Fixed by JuliaImGui/ImGuiOpenGLBackend.jl#2
Closed

Memory leak #64

alenskorobogatova opened this issue Oct 15, 2021 · 15 comments · Fixed by JuliaImGui/ImGuiOpenGLBackend.jl#2

Comments

@alenskorobogatova
Copy link
Contributor

Memory leak when launching examples of the master branch of CImGui (launch occurs as described in the readme file)

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 15, 2021

Which example?

@sairus7
Copy link
Collaborator

sairus7 commented Oct 15, 2021

Confirm memory leak at Windows 10, both from demo/demo.jl and examples/demo.jl.

@sairus7
Copy link
Collaborator

sairus7 commented Oct 15, 2021

Looks like I didn't notice this leak in this PR, since there is very slow operating memory increase of Julia process. This bug also present in this ImPlot branch.

Looked at both demo.jl code but I have no idea why it happens, maybe because I just commented glfwSetErrorCallback (didn't know what argument it takes) or misused ImGuiGLFWBackend.create_context and ImGuiOpenGLBackend.create_context functions, or there are deeper issues with newly generated code? @Gnimuc can you take a look at this?

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 16, 2021

only on Windows? does the problem persist if you disable the docking and multi-viewport features?

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 16, 2021

The leak is probably due to this line in the backend: https://github.com/JuliaImGui/ImGuiGLFWBackend.jl/blob/a005da40908b1d5b1335762ef0723bae570453f8/src/interface.jl#L207

The implementation assumes the C-managed Ptr{ImGuiPlatformMonitor} pointer is initilized as a C_NULL, so there is no need to manually free it on the Julia side before we fill up it with the new resources. I've checked the related IMGUI code before, but I might be wrong as well.

@Gnimuc Gnimuc added bug Something isn't working help wanted Extra attention is needed labels Oct 16, 2021
@sairus7
Copy link
Collaborator

sairus7 commented Oct 16, 2021

I'm not sure if I understand you corectly, do you mean that monitors_ptr ownership is passed to platform_io object that is gc'ed by C library?

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 16, 2021

Not gc-ed, but free-ed by C/C++. As platform_io is an object managed by the C/C++ library, I guess the C++ code does something like:

if (platform_io.monitors_ptr)
    delete[] platform_io.monitors_ptr

This is why I allocated monitors_ptr through Libc.malloc.

@sairus7
Copy link
Collaborator

sairus7 commented Oct 16, 2021

But this is called only once - how can it cause constant memory increase inside rendering cycle?

Can it be somehow related to this bug with arrays or pointers? JuliaImGui/ImPlot.jl#23

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 16, 2021

By memory leak, do you mean "memory constantly increasing when running the GUI window" or "memory is not released correctly after GUI window is closed". I would like to know whether the leak is on the C side or the Julia side. A MWE example would be helpful here.

@sairus7
Copy link
Collaborator

sairus7 commented Oct 16, 2021

I mean, memory constantly increasing when running the GUI window.
It repeats with most examples, lets take short one from ImGuiGLFWBackend readme, just an empty window with no widgets, no multiport or docking. It starts with 360 MB of RAM and after 1 minute grows up to 1017 MB. (~11MB/sec).

using ImGuiGLFWBackend
using ImGuiGLFWBackend.LibCImGui
using ImGuiGLFWBackend.LibGLFW
using ImGuiOpenGLBackend
using ImGuiOpenGLBackend.ModernGL

# create contexts
imgui_ctx = igCreateContext(C_NULL)

window_ctx = ImGuiGLFWBackend.create_context()
window = ImGuiGLFWBackend.get_window(window_ctx)

gl_ctx = ImGuiOpenGLBackend.create_context()

# enable docking and multi-viewport
# io = igGetIO()
# io.ConfigFlags = unsafe_load(io.ConfigFlags) | ImGuiConfigFlags_DockingEnable
# io.ConfigFlags = unsafe_load(io.ConfigFlags) | ImGuiConfigFlags_ViewportsEnable

# set style
igStyleColorsDark(C_NULL)

# init
ImGuiGLFWBackend.init(window_ctx)
ImGuiOpenGLBackend.init(gl_ctx)

try
    while glfwWindowShouldClose(window) == GLFW_FALSE
        glfwPollEvents()
        # new frame
        ImGuiOpenGLBackend.new_frame(gl_ctx)
        ImGuiGLFWBackend.new_frame(window_ctx)
        igNewFrame()

        # UIs
        # igShowDemoWindow(Ref(true))
        # igShowMetricsWindow(Ref(true))

        # rendering
        igRender()
        glfwMakeContextCurrent(window)
        w_ref, h_ref = Ref{Cint}(0), Ref{Cint}(0)
        glfwGetFramebufferSize(window, w_ref, h_ref)
        display_w, display_h = w_ref[], h_ref[]
        glViewport(0, 0, display_w, display_h)
        glClearColor(0.45, 0.55, 0.60, 1.00)
        glClear(GL_COLOR_BUFFER_BIT)
        ImGuiOpenGLBackend.render(gl_ctx)

        # if unsafe_load(igGetIO().ConfigFlags) & ImGuiConfigFlags_ViewportsEnable == ImGuiConfigFlags_ViewportsEnable
        #     backup_current_context = glfwGetCurrentContext()
        #     igUpdatePlatformWindows()
        #     GC.@preserve gl_ctx igRenderPlatformWindowsDefault(C_NULL, pointer_from_objref(gl_ctx))
        #     glfwMakeContextCurrent(backup_current_context)
        # end

        glfwSwapBuffers(window)
    end
catch e
    @error "Error in renderloop!" exception=e
    Base.show_backtrace(stderr, catch_backtrace())
finally
    ImGuiOpenGLBackend.shutdown(gl_ctx)
    ImGuiGLFWBackend.shutdown(window_ctx)
    igDestroyContext(imgui_ctx)
    glfwDestroyWindow(window)
end

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 16, 2021

Thanks for the example! I can reproduce it on my machine as well. Trying to looking into it...

@sairus7
Copy link
Collaborator

sairus7 commented Oct 16, 2021

Some effects:

  • Without any windows open, memory increase in 1 minute: 270 MB -- 922 MB (~11 MB/sec).
  • With some window (demo or metrics), memory increase in 1 minute: 270 MB -- 1180 MB (~15 MB/sec).
  • Repeated runs of this script from the same Julia console increases initial GUI process memory.

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 16, 2021

Thanks for reporting the issue! I've just tagged a new version which should be released within a hour.

@sairus7
Copy link
Collaborator

sairus7 commented Oct 16, 2021

I've tried the same example with dev ImGuiOpenGLBackend, and now it is not growing in memory but produces a bunch of the following errors from time to time:

┌ Error: GLFW ERROR: code 65544 msg: WGL: Failed to make context current: The handle is invalid.
└ @ ImGuiGLFWBackend C:\Users\user\.julia\packages\ImGuiGLFWBackend\42XtF\src\callbacks.jl:2
┌ Error: GLFW ERROR: code 65544 msg: WGL: Failed to make context current: The requested transformation operation is not supported.
└ @ ImGuiGLFWBackend C:\Users\user\.julia\packages\ImGuiGLFWBackend\42XtF\src\callbacks.jl:2

@Gnimuc
Copy link
Owner

Gnimuc commented Oct 17, 2021

I can not reproduce that on macOS, feel free to file a new issue.

Does this happen without the new patch? If so, you could apply the patch right above this line:

https://github.com/JuliaImGui/ImGuiOpenGLBackend.jl/blob/master/src/device.jl#L114

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants