瀏覽代碼

Textures: fixed a crash if a texture marked as _WantDestroy by a backend after it had already been destroyed. (#8977, #8811)

ocornut 1 周之前
父節點
當前提交
ea564a6a5e
共有 2 個文件被更改,包括 21 次插入13 次删除
  1. 3 0
      docs/CHANGELOG.txt
  2. 18 13
      imgui_draw.cpp

+ 3 - 0
docs/CHANGELOG.txt

@@ -69,6 +69,9 @@ Other Changes:
 - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode,
 - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode,
   Android Studio & more) to provide nicer display for ImVec2, ImVec4, ImVector etc.
   Android Studio & more) to provide nicer display for ImVec2, ImVec4, ImVector etc.
   See misc/debuggers/ for details. (#8950) [@mentlerd]
   See misc/debuggers/ for details. (#8950) [@mentlerd]
+- Textures: fixed a crash if a texture marked as _WantDestroy by a backend after
+  it had already been destroyed. This would typically happen when calling backend's
+  ImGui_ImplXXXX_InvalidateDeviceObjects() helpers twice in a row. (#8977, #8811)
 - Textures: fixed an issue preventing multi-contexts sharing a ImFontAtlas from
 - Textures: fixed an issue preventing multi-contexts sharing a ImFontAtlas from
   being possible to destroy in any order.
   being possible to destroy in any order.
 - Textures: fixed not updating ImTextureData's RefCount when destroying a context
 - Textures: fixed not updating ImTextureData's RefCount when destroying a context

+ 18 - 13
imgui_draw.cpp

@@ -2790,21 +2790,29 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere
         if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)
         if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)
             IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK.");
             IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK.");
 
 
+        // Request destroy
+        // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
+        // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
+        if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy)
+        {
+            IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
+            tex->Status = ImTextureStatus_WantDestroy;
+        }
+
+        // If a texture has never reached the backend, they don't need to know about it.
+        // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy
+        //  when invalidating graphics objects twice, which would previously remove it from the list and crash.)
+        if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
+            tex->Status = ImTextureStatus_Destroyed;
+
+        // Process texture being destroyed
         if (tex->Status == ImTextureStatus_Destroyed)
         if (tex->Status == ImTextureStatus_Destroyed)
         {
         {
             IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!");
             IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!");
             if (tex->WantDestroyNextFrame)
             if (tex->WantDestroyNextFrame)
                 remove_from_list = true; // Destroy was scheduled by us
                 remove_from_list = true; // Destroy was scheduled by us
             else
             else
-                tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend (e.g. freed resources mid-run)
-        }
-        else if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_WantDestroy)
-        {
-            // Request destroy.
-            // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
-            // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
-            IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
-            tex->Status = ImTextureStatus_WantDestroy;
+                tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run)
         }
         }
 
 
         // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
         // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
@@ -2812,13 +2820,10 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere
         if (tex->Status == ImTextureStatus_WantDestroy)
         if (tex->Status == ImTextureStatus_WantDestroy)
             tex->UnusedFrames++;
             tex->UnusedFrames++;
 
 
-        // If a texture has never reached the backend, they don't need to know about it.
-        if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
-            remove_from_list = true;
-
         // Destroy and remove
         // Destroy and remove
         if (remove_from_list)
         if (remove_from_list)
         {
         {
+            IM_ASSERT(atlas->TexData != tex);
             tex->DestroyPixels();
             tex->DestroyPixels();
             IM_DELETE(tex);
             IM_DELETE(tex);
             atlas->TexList.erase(atlas->TexList.begin() + tex_n);
             atlas->TexList.erase(atlas->TexList.begin() + tex_n);