GS: Better handle imgui texture creation hazards.

This commit is contained in:
lightningterror
2026-07-04 20:40:55 +02:00
parent b6e9a04433
commit a4b11651a4
+53 -54
View File
@@ -502,68 +502,67 @@ void GSDevice::UpdateImGuiTextures()
case ImTextureStatus_Destroyed:
continue;
case ImTextureStatus_WantCreate:
{
GSTexture* gs_tex = g_gs_device->CreateTexture(im_tex->Width, im_tex->Height, 1, GSTexture::Format::Color);
if (!gs_tex)
pxFailRel("Failed to create ImGui texture");
im_tex->SetTexID(reinterpret_cast<ImTextureID>(gs_tex->GetNativeHandle()));
im_tex->BackendUserData = gs_tex;
[[fallthrough]];
}
case ImTextureStatus_WantUpdates:
{
// If we fell through from WantCreate, then we are uploading the full size
// Otherwise, we are just updating the specified region
// clange-format off
const int upload_x = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.x;
const int upload_y = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.y;
const int upload_w = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Width : im_tex->UpdateRect.w;
const int upload_h = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Height : im_tex->UpdateRect.h;
const int upload_pitch = upload_w * im_tex->BytesPerPixel;
// clange-format on
const GSVector4i rect{
upload_x,
upload_y,
upload_x + upload_w,
upload_y + upload_h,
};
GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData);
GSTexture::GSMap map;
if (gs_tex->Map(map, &rect))
if (GSTexture* gs_tex = g_gs_device->CreateTexture(im_tex->Width, im_tex->Height, 1, GSTexture::Format::Color))
{
for (int y = 0; y < upload_h; y++)
std::memcpy(map.bits + map.pitch * y, im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
gs_tex->Unmap();
im_tex->SetTexID(reinterpret_cast<ImTextureID>(gs_tex->GetNativeHandle()));
im_tex->BackendUserData = gs_tex;
}
else
{
for (int y = 0; y < upload_h; y++)
gs_tex->Update({rect.left, rect.top + y, rect.right, rect.top + y + 1},
im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
}
im_tex->Status = ImTextureStatus_OK;
break;
}
case ImTextureStatus_WantDestroy:
{
GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData);
if (gs_tex == nullptr)
pxFailRel("Failed to create ImGui texture");
break;
}
[[fallthrough]];
case ImTextureStatus_WantUpdates:
if (GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData))
{
// If we fell through from WantCreate, then we are uploading the full size
// Otherwise, we are just updating the specified region
// clange-format off
const int upload_x = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.x;
const int upload_y = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.y;
const int upload_w = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Width : im_tex->UpdateRect.w;
const int upload_h = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Height : im_tex->UpdateRect.h;
const int upload_pitch = upload_w * im_tex->BytesPerPixel;
// clange-format on
// While it's unlikely we're going to reuse the same size as imgui for rendering,
// imgui may request a new atlas of the same size if old font sizes are evicted.
Recycle(gs_tex);
const GSVector4i rect{
upload_x,
upload_y,
upload_x + upload_w,
upload_y + upload_h,
};
im_tex->SetTexID(ImTextureID_Invalid);
im_tex->BackendUserData = nullptr;
im_tex->Status = ImTextureStatus_Destroyed;
GSTexture::GSMap map;
if (gs_tex->Map(map, &rect))
{
for (int y = 0; y < upload_h; y++)
std::memcpy(map.bits + map.pitch * y, im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
gs_tex->Unmap();
}
else
{
for (int y = 0; y < upload_h; y++)
gs_tex->Update({rect.left, rect.top + y, rect.right, rect.top + y + 1},
im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
}
im_tex->Status = ImTextureStatus_OK;
}
break;
case ImTextureStatus_WantDestroy:
if (GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData))
{
// While it's unlikely we're going to reuse the same size as imgui for rendering,
// imgui may request a new atlas of the same size if old font sizes are evicted.
Recycle(gs_tex);
im_tex->SetTexID(ImTextureID_Invalid);
im_tex->BackendUserData = nullptr;
im_tex->Status = ImTextureStatus_Destroyed;
}
break;
}
default:
pxAssert(false);
break;