thin3d: add sub-rectangle texture updates, use them for imgui fonts

imgui 1.92 hands the backend a list of dirty rectangles when its font atlas
grows, but thin3d could only replace a whole mip level, so every new glyph
re-uploaded the entire atlas.

Adds DrawContext::UpdateTextureRegions, taking a batch of regions so each
backend can submit them together:

- Vulkan: packs the regions into the push pool and issues a single
  vkCmdCopyBufferToImage from the init command buffer, transitioning only
  the level being written.
- OpenGL: new TEXTURE_SUBIMAGE init step. The existing sub-image path is a
  render command needing an active render pass and a texture slot, which
  doesn't fit here. Rows are packed caller-side since GLES2 lacks
  GL_UNPACK_ROW_LENGTH.
- D3D11: UpdateSubresource with a box, no staging texture needed.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-09-12 13:16:17 -06:00
1 parent 43850e7c03
commit 70095e458b
10 files changed
+204 -9

No files matched your search

+18 -5
View File
@@ -53,9 +53,9 @@ static BackendData *ImGui_ImplThin3d_GetBackendData() {
}
// Since 1.92 imgui owns its textures (the font atlas, and any more it needs as glyphs get
// rasterized on demand) and asks the backend to create, refresh and destroy them. thin3d can only
// replace a whole mip level, not a sub-rectangle, so an update re-uploads everything - which is
// fine, since these only change when a new glyph shows up.
// rasterized on demand) and asks the backend to create, refresh and destroy them. An update comes
// with a list of dirty rectangles, which we hand to thin3d as sub-rectangle uploads so a single new
// glyph doesn't cost a re-upload of the whole atlas.
static void ImGui_ImplThin3d_UpdateTexture(Draw::DrawContext *draw, ImTextureData *tex) {
BackendData *bd = ImGui_ImplThin3d_GetBackendData();
@@ -103,8 +103,21 @@ static void ImGui_ImplThin3d_UpdateTexture(Draw::DrawContext *draw, ImTextureDat
ERROR_LOG(Log::System, "imgui: asked to update texture %d, which we don't have", (int)tex->GetTexID());
return;
}
const uint8_t *data = (const uint8_t *)tex->GetPixels();
draw->UpdateTextureLevels(bd->imguiTextures[index], &data, nullptr, 1);
std::vector<Draw::TextureRegionUpdate> regions;
regions.reserve(tex->Updates.Size);
for (const ImTextureRect &rect : tex->Updates) {
Draw::TextureRegionUpdate region;
region.x = rect.x;
region.y = rect.y;
region.w = rect.w;
region.h = rect.h;
region.data = (const uint8_t *)tex->GetPixelsAt(rect.x, rect.y);
region.byteStride = tex->GetPitch();
regions.push_back(region);
}
if (!regions.empty()) {
draw->UpdateTextureRegions(bd->imguiTextures[index], 0, regions.data(), (int)regions.size());
}
tex->SetStatus(ImTextureStatus_OK);
break;
}