添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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 Branch: docking

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_glfw.h.cpp + imgui_impl_opengl3.cpp
Compiler: MSVC 2019
Operating System: Windows 10

My Issue/Question:
I have an inspector panel on the right that will show properties based on the type of content selected. For textures, it'll just show a preview of the texture using ImGui::Image() . When I click on a texture, my memory usage shoots up and climbs until I run out of memory. If I selected something else removing the image preview, the memory isn't released. The FPS also drops significantly any time an image preview is being shown (from 1100+ FPS down to <40 FPS).

Screenshots/Video
https://user-images.githubusercontent.com/19679090/135903515-d2af99a0-5510-4ad4-b296-e1addd6071da.mp4

The function that loads the image from a file and creates the texture (straight from the ImGui docs):

bool LSUtilities::LoadTextureFromFile(const char* filename, GLuint* out_texture, int* out_width, int* out_height) {
	int image_width = 0;
	int image_height = 0;
	unsigned char* image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);
	if (image_data == NULL)
		return false;
	GLuint image_texture;
	glGenTextures(1, &image_texture);
	glBindTexture(GL_TEXTURE_2D, image_texture);
	// Setup filtering parameters for display
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	// Upload pixels into texture
#if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__)
	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
	stbi_image_free(image_data);
	*out_texture = image_texture;
	*out_width = image_width;
	*out_height = image_height;
	glBindTexture(GL_TEXTURE_2D, 0);
	return true;

The code that uses this function to create the image preview (this is inside the render loop):

  int texture_preview_width = 0;
  int texture_preview_height = 0;
  GLuint texture_preview = 0;
  bool ret = LSUtilities::LoadTextureFromFile(Resources.textures[selectedItemID].c_str(), &texture_preview, &texture_preview_width, &texture_preview_height);
  IM_ASSERT(ret);
  ImVec2 previewSize = {
    ImGui::GetContentRegionAvailWidth(),
    (texture_preview_height * ImGui::GetContentRegionAvailWidth()) / texture_preview_width
  ImGui::Image((ImTextureID)texture_preview, previewSize);

I've tried deleting the texture with glDeleteTextures(1, &texture_preview) after the ImGui::Image call but the code above is inside the render loop and that appears to just delete the texture before ImGui can load it.

You should load the texture from outside the main loop (during init or by demand later), save the textureId somewhere and render it in mainloop.

If you create the texture again and again on each frame you are exhausting the memory, and ImGUI defer all drawings to the end of the Frame so you must not delete the texture inside the mainloop before ImGui renders.

selected = get_user_selection_on_some_event(...) // Check if image selection has actually changed (by the euser) and update cache ONLY in that case if selected != selected_cache: selected_cache = selected textureId_cache = load_texture_from_disk(selected_cache) // render the cached texture if any if textureId_cache: render_texture(textureId_cache) if textureId_cache: free_texture( textureId_cache )

There is no problem calling opengl from mainloop, the problem is to load the texture each time.

PS: In realworld applications you should load your assets in a different thread for optimal performance anyway.

I think I sorted it. I moved the following code to outside the main loop:

GLuint texture_preview;
glGenTextures(1, &texture_preview);
glBindTexture(GL_TEXTURE_2D, texture_preview);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

and then in the mainloop:

  int image_width = 0;
  int image_height = 0;
  unsigned char* image_data = stbi_load(Resources.textures[selectedItemID].c_str(), &image_width, &image_height, NULL, 4);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
  stbi_image_free(image_data);
  ImVec2 previewSize = {
    ImGui::GetContentRegionAvailWidth(),
    (image_height * ImGui::GetContentRegionAvailWidth()) / image_width
  ImGui::Image((ImTextureID)texture_preview, previewSize);

I then unbind and delete at the end outside the render loop. Fixes the memory leak. However, the memory allocated isn't freed when I select something that isn't an image. Not a huge concern as memory allocated is less than 1MB usually but still feels inefficient.

How would I defer the stbi_load call to outside the main loop if it needs a path?

#4628 (comment)

This will not put it outside the mainloop which is not required, but it will use it only as needed, without unnecessary reloading. To move it out of the mainloop you need another thread. But you can leave it single threaded by now, this is really basic stuff.