Languages: [EN] English | [KO] 한국어
Per-Thread ImGui Contexts via TLS: A Working Implementation with Multiple Backends
TLS를 통한 스레드별 ImGui 컨텍스트: 다중 백엔드를 지원하는 작동 구현
Overview
소개
Implementing multi-window based on multi-backend without multi-viewport in Dear ImGui
Initially, TLS was used simply to reuse engine, launcher, and UI code
Dear ImGui 에서 멀티뷰포트 없이 멀티백앤드 기반 다중 창을 구현
최초에는 단순히 엔진, 런처, UI 코드를 재사용하기 위해 TLS 사용
Screenshot
작동 화면
- Turned on the outline so you can see that it's an individual window.
- Right: FE_DASHBOARD (SYSTEM)
- Top left: FE_OSD (USER)
- Middle left: FE_RING (USER)
- 개별 창임을 확인할 수 있게 외곽선을 활성화
- 오른쪽 FE_DASHBOARD(시스템)
- 왼쪽 상단 FE_OSD(사용자)
- 왼쪽 가운데 FE_RING(사용자)
Excluding Multi-Viewport
- Already implemented the transparency of the auxiliary window with DirectX 11+multi-viewport, but decided to exclude it due to the following issues
- The basic implementation is simple, but the control code grows rapidly when non-standard requirements are added
- Separate clear code is required to achieve transparency
- Related code differs completely per API, causing abstraction cost to skyrocket
멀티뷰포트 제외
- 이미 DirectX 11+멀티뷰포트로 보조 창의 투명화를 구현 하였지만 다음의 문제들로 인하여 제외 결정
- 기본 구현은 단순하지만, 비표준 요구사항이 추가될 경우 제어 코드가 빠르게 증가
- 투명화를 위해 별도 클리어 코드, 백앤드 수정
- API 별 코드 구조가 완전히 달라 추상화 비용 급증
Multi-Backend
- Multiplatform porting costs skyrocket when developing for DirectX 11
- Add an abstraction layer for adding various APIs
- Add: OpenGL, Vulkan, DirectX11, DirectX12
- API Implementation LOC:
- OpenGL: 300 LOC
- DirectX11: 300 LOC
- DirectX12: 800 LOC
- Vulkan: 1200 LOC
- The difficulty of Vulkan and DirectX12 was beyond imagination
멀티백앤드
- DirectX 11 기준 개발 시 멀티플랫폼 포팅 비용 급증
- 다양한 API추가를 위한 추상화 계층 도입
- 추가: OpenGL, Vulkan, DirectX11, DirectX12
- API 구현부 코드량(LOC):
- OpenGL: 300 LOC
- DirectX11: 300 LOC
- DirectX12: 800 LOC
- Vulkan: 1200 LOC
- Vulkan, DirectX12 의 난이도는 상상 이상
Dear ImGui
- ImGui is based on a single global context, causing issues when using multiple windows in a single program
- Multi-viewport is provided, but the moment functionality beyond the basics is needed, control code grows rapidly
- Implementing certain features requires separate clear handling, and the required code differs per API
- imgui.cpp itself mentions using extern thread_local ImGuiContext* MyImGuiTLS; for thread usage
Dear ImGui
- ImGui 는 단일 전역 컨텍스트 기반으로 단일 프로그램에서 다중 창 사용시 문제가 발생
- 멀티뷰포트 기능을 제공하고 있지만,기본 기능 외 기능이 필요해지는 순간 컨트롤을 위한 코드가 급증
- 특정 기능을 구현할 경우 별도 클리어가 필요하며, API마다 필요한 코드가 다름
- imgui.cpp 에도 쓰레드 에서 사용할시 extern thread_local ImGuiContext* MyImGuiTLS; 언급
Conditions
- Code reuse -> Common code for engine, launcher, and ImGui
- No pre-configuration -> No configuration before TLS entry
- No arguments -> No arguments/structs passed
- Single entry point -> Calls each UI start function registered with the scheduler
조건
- 코드 재사용 -> 엔진, 런처, ImGui 공통 코드
- 사전 설정 불가 -> TLS 진입 전 설정 불가
- 무 인자 -> 인자/구조체 전달 불가
- 단일 진입점 -> 스케줄러에 등록 된 각 UI 시작 함수 호출
Getting things running (minimum)
- ImGui:
- ImGui only needs config settings.
- Add the following three lines to imconfig.h.
- struct ImGuiContext;
- extern thread_local ImGuiContext* MyImGuiTLS;
- #define GImGui MyImGuiTLS
- Add the following to one of your project's cpp files.
- thread_local ImGuiContext* MyImGuiTLS = NULL;
- Program Design:
- Multi-thread Scheduler(possible by just creating a thread)
- Common global app context (configuring each individual UI)
- Global Resources Manager (for dynamic calls to OpenGL, Vulkan, DirectX11, and DirectX12)
- Implementation Requirements:
- TLS-based function pointer table structure engine (common code for graphics APIs)
- TLS-based function pointer table structure launcher (common code such as UI loop, window creation, Proc, etc.)
- TLS-based function pointer table structure UI library (common UI code)
- TLS-based function pointer table structure UI implementation
- TLS-based function pointer engine + launcher + UI -> Function pointer chaining code for a single thread to tie everything together
- TLS-based function pointer engine + launcher + UI -> "TLS* data = &public global app context" bind code "inside" pointer chain
작동 준비(최소사항)
- ImGui:
- ImGui 는 config 만 설정하면 완료
- imconfig.h 에 다음 3라인을 추가
- struct ImGuiContext;
- extern thread_local ImGuiContext* MyImGuiTLS;
- #define GImGui MyImGuiTLS
- 프로젝트 cpp중 하나에 다음 내용 추가
- thread_local ImGuiContext* MyImGuiTLS = NULL;
- 프로그램 설계:
- 멀티 쓰레드 스케줄러(쓰레드 생성만 해도 가능)
- 공용 전역 앱 컨텍스트(설정 파일 -> 개별 UI 설정 -> 공용 전역 앱 컨텍스트)
- 전역 리소스 매니저(opengl, vulkan, directx11, directx12 동적 호출용)
- 구현 요구:
- TLS 기반 함수 포인터 테이블 구조 엔진 (그래픽 API 공통 코드)
- TLS 기반 함수 포인터 테이블 구조 런처 (UI 루프, 창 생성, Proc 등 공통 코드)
- TLS 기반 함수 포인터 테이블 구조 UI 라이브러리 (UI 공통 코드)
- TLS 기반 함수 포인터 테이블 구조 UI 구현부
- TLS 기반 함수 포인터 엔진+런처+UI -> 전체를 묶어줄 쓰레드 단일 진입용 함수 포인터 체인 코드
- TLS 기반 함수 포인터 엔진+런처+UI -> 포인터 체이 "내부" 에서 "TLS* 데이터 = &공용 전역 앱 컨텍스트" 바인드 코드
How It Works
- All processes are connected via zero-argument function pointer chaining
- Start:
- Scheduler -> Registered task -> Create thread
- UI start (data pointer binding)
- Launcher (window creation)
- Engine (API setting detection and initialization)
- Launcher (dispatch execution)
- UI loop begins
- Shutdown:
- Scheduler -> Registered shutdown function executed
- Shutdown called
- Launcher shutdown
- Engine shutdown
- UI data cleanup
- ImGui context cleanup
- Scheduler -> Thread terminated
- Restart:
- Function pointer replacement only, no thread intervention
- Shutdown + start process executed in continuous chaining
- Engine reads bound API setting value
- API function pointer replaced -> execution begins
작동 방식
- 모든 과정은 인자 없는 함수 포인터 체이닝으로 연결
- 시작:
- 스케줄러 -> 등록된 작업 -> 쓰레드 생성
- UI 시작(데이터 포인터 바인딩)
- 런처(창 생성)
- 엔진(API 설정 감지 및 초기화)
- 런처(디스패치 실행)
- UI 루프 시작
- 종료:
- 스케줄러 -> 등록된 셧다운 함수 실행
- 셧다운 호출
- 런처 종료
- 엔진 종료
- UI 데이터 정리
- ImGui 컨텍스트 정리
- 스케줄러 -> 쓰레드 종료
- 재시작:
- 쓰레드 개입 없이 함수 포인터 교체만 실행
- 종료+시작 과정을 연속 체이닝
- 엔진에서 바인딩된 API 설정 값 참조
- API 함수 포인터 교체 -> 실행
Afterword
- The following is a metaphor and does not equate to an internal implementation
- Multiviewport: Similar to automatic dispatch via virtual tables (convenient, but less granular control)
- TLS approach: Manual function pointer tables (more complex, but full control)
- It is convenient to treat the TLS isolation structure covered here as a type of virtualization, VM concept(closer to the chroot/jail concept)
마무리
- 다음 내용은 비유적 표현이며 내부 구현을 동일시하는 의미는 아님
- 멀티뷰포트: 가상 테이블을 통한 자동 디스패치와 유사함(편리하지만 세부적인 제어는 제한적)
- TLS 방식: 수동 함수 포인터 테이블(더 복잡하지만 완벽한 제어 가능)
- 여기서 다루는 TLS 격리 구조는 일종의 가상화, VM 개념으로 다루는게 편리(chroot/jail 개념에 더 가까움)
partial log
로그 일부분
[2026-03-04 19:16:25.924] [CORE ][ OK ][Success][Libman ] => UnLoaded OPENGL: 0, caller: OPENGL
[2026-03-04 19:16:25.933] [FRONTEND ][EVNT][Success][Windows ] => Shutdown Frontend Luncher
[2026-03-04 19:16:25.934] [FRONTEND ][EVNT][Success][Windows ] => UI - Reload
[2026-03-04 19:16:25.934] [FRONTEND ][EVNT][Waiting][Windows ] => Creating UI
[2026-03-04 19:16:25.935] [FRONTEND ][ OK ][Online ][Windows ] => Init - Create Window
[2026-03-04 19:16:25.939] [FRONTEND ][ OK ][Online ][Windows ] => Init - Update Style
[2026-03-04 19:16:25.940] [FRONTEND ][ OK ][Online ][Windows ] => Init - Engine
[2026-03-04 19:16:25.940] [FRONTEND ][INFO][Success][Windows ] => Graphic API: Vulkan
[2026-03-04 19:16:26.356] [FRONTEND ][ OK ][Online ][Windows ] => Init - Font
[2026-03-04 19:16:26.361] [FRONTEND ][ OK ][Online ][Windows ] => Init - Background
[2026-03-04 19:16:26.417] [FRONTEND ][ OK ][Online ][Windows ] => Init - Theme
[2026-03-04 19:16:26.418] [FRONTEND ][ OK ][Online ][Windows ] => Theme File Loaded: Gouki
[2026-03-04 19:16:26.438] [FRONTEND ][ OK ][Online ][Windows ] => Init - Done
[2026-03-04 19:16:36.305] [FRONTEND ][EVNT][Success][Windows ] => Shutdown Frontend Luncher
[2026-03-04 19:16:36.305] [FRONTEND ][EVNT][Success][Windows ] => UI - Reload
[2026-03-04 19:16:36.306] [FRONTEND ][EVNT][Waiting][Windows ] => Creating UI
[2026-03-04 19:16:36.306] [FRONTEND ][ OK ][Online ][Windows ] => Init - Create Window
[2026-03-04 19:16:36.311] [FRONTEND ][ OK ][Online ][Windows ] => Init - Update Style
[2026-03-04 19:16:36.311] [FRONTEND ][ OK ][Online ][Windows ] => Init - Engine
[2026-03-04 19:16:36.312] [FRONTEND ][INFO][Success][Windows ] => Graphic API: DirectX 11
[2026-03-04 19:16:36.313] [CORE ][ OK ][Success][Libman ] => Loaded D3D11: 1, caller: D3D11
[2026-03-04 19:16:36.369] [FRONTEND ][ OK ][Online ][Windows ] => Init - Font
[2026-03-04 19:16:36.380] [FRONTEND ][ OK ][Online ][Windows ] => Init - Background
[2026-03-04 19:16:36.425] [FRONTEND ][ OK ][Online ][Windows ] => Init - Theme
[2026-03-04 19:16:36.897] [FRONTEND ][ OK ][Online ][Windows ] => Theme File Loaded: Gouki
[2026-03-04 19:16:36.921] [FRONTEND ][ OK ][Online ][Windows ] => Init - Done
[2026-03-04 19:16:43.454] [CORE ][ OK ][Success][Libman ] => UnLoaded D3D11: 0, caller: D3D11
[2026-03-04 19:16:43.465] [FRONTEND ][EVNT][Success][Windows ] => Shutdown Frontend Luncher
[2026-03-04 19:16:43.465] [FRONTEND ][EVNT][Success][Windows ] => UI - Reload
[2026-03-04 19:16:43.466] [FRONTEND ][EVNT][Waiting][Windows ] => Creating UI
[2026-03-04 19:16:43.467] [FRONTEND ][ OK ][Online ][Windows ] => Init - Create Window
[2026-03-04 19:16:43.471] [FRONTEND ][ OK ][Online ][Windows ] => Init - Update Style
[2026-03-04 19:16:43.471] [FRONTEND ][ OK ][Online ][Windows ] => Init - Engine
[2026-03-04 19:16:43.471] [FRONTEND ][INFO][Success][Windows ] => Graphic API: DirectX 12
[2026-03-04 19:16:43.473] [CORE ][ OK ][Success][Libman ] => Loaded D3D12: 1, caller: D3D12
[2026-03-04 19:16:43.546] [CORE ][ OK ][Success][Libman ] => Loaded DCOMP: 1, caller: D3D12
[2026-03-04 19:16:43.547] [FRONTEND ][ OK ][Online ][Windows ] => Init - Font
[2026-03-04 19:16:43.561] [FRONTEND ][ OK ][Online ][Windows ] => Init - Background
[2026-03-04 19:16:43.598] [FRONTEND ][ OK ][Online ][Windows ] => Init - Theme
[2026-03-04 19:16:43.599] [FRONTEND ][ OK ][Online ][Windows ] => Theme File Loaded: Gouki
[2026-03-04 19:16:43.613] [FRONTEND ][ OK ][Online ][Windows ] => Init - Done
[2026-03-04 19:16:53.171] [UTILITY ][ OK ][Online ][Scheduler ] => Added - PID:1140 (FE_OSD)
[2026-03-04 19:16:53.172] [UTILITY ][ OK ][Success][Scheduler ] => Service 'FE_OSD' registered (PID: 1140)
[2026-03-04 19:16:53.172] [FRONTEND ][ OK ][Online ][Windows ] => Init - OSD
[2026-03-04 19:16:53.173] [FRONTEND ][EVNT][Waiting][Windows ] => Creating UI
[2026-03-04 19:16:53.173] [FRONTEND ][ OK ][Online ][Windows ] => Init - Create Window
[2026-03-04 19:16:53.177] [FRONTEND ][ OK ][Online ][Windows ] => Init - Update Style
[2026-03-04 19:16:53.177] [FRONTEND ][ OK ][Online ][Windows ] => Init - Engine
[2026-03-04 19:16:53.177] [FRONTEND ][INFO][Success][Windows ] => Graphic API: OpenGL
[2026-03-04 19:16:53.178] [CORE ][ OK ][Success][Libman ] => Loaded OPENGL: 1, caller: OPENGL
[2026-03-04 19:16:53.189] [FRONTEND ][ OK ][Online ][Windows ] => Init - Font
[2026-03-04 19:16:53.241] [FRONTEND ][ OK ][Online ][Windows ] => Init - Background
[2026-03-04 19:16:53.242] [FRONTEND ][ OK ][Online ][Windows ] => Init - Theme
[2026-03-04 19:16:53.250] [FRONTEND ][ OK ][Online ][Windows ] => Init - Done
[2026-03-04 19:16:53.742] [UTILITY ][ OK ][Online ][Scheduler ] => Added - PID:1150 (FE_RING)
[2026-03-04 19:16:53.742] [FRONTEND ][ OK ][Online ][Windows ] => Init - Ring
[2026-03-04 19:16:53.743] [FRONTEND ][EVNT][Waiting][Windows ] => Creating UI
[2026-03-04 19:16:53.743] [UTILITY ][ OK ][Success][Scheduler ] => Service 'FE_RING' registered (PID: 1150)
[2026-03-04 19:16:53.743] [FRONTEND ][ OK ][Online ][Windows ] => Init - Create Window
[2026-03-04 19:16:53.748] [FRONTEND ][ OK ][Online ][Windows ] => Init - Update Style
[2026-03-04 19:16:53.749] [FRONTEND ][ OK ][Online ][Windows ] => Init - Engine
[2026-03-04 19:16:53.749] [FRONTEND ][INFO][Success][Windows ] => Graphic API: DirectX 11
[2026-03-04 19:16:53.750] [CORE ][ OK ][Success][Libman ] => Loaded D3D11: 1, caller: D3D11
[2026-03-04 19:16:53.805] [FRONTEND ][ OK ][Online ][Windows ] => Init - Font
[2026-03-04 19:16:53.817] [FRONTEND ][ OK ][Online ][Windows ] => Init - Background
[2026-03-04 19:16:53.817] [FRONTEND ][ OK ][Online ][Windows ] => Init - Theme
[2026-03-04 19:16:53.825] [FRONTEND ][ OK ][Online ][Windows ] => Init - Done
[2026-03-04 19:17:00.648] [CORE ][ OK ][Success][Libman ] => UnLoaded DCOMP: 0, caller: D3D12
[2026-03-04 19:17:00.668] [CORE ][ OK ][Success][Libman ] => UnLoaded D3D12: 0, caller: D3D12
[2026-03-04 19:17:00.676] [FRONTEND ][EVNT][Success][Windows ] => Shutdown Frontend Luncher
[2026-03-04 19:17:00.676] [FRONTEND ][EVNT][Success][Windows ] => UI - Reload
[2026-03-04 19:17:00.677] [FRONTEND ][EVNT][Waiting][Windows ] => Creating UI
[2026-03-04 19:17:00.677] [FRONTEND ][ OK ][Online ][Windows ] => Init - Create Window
[2026-03-04 19:17:00.682] [FRONTEND ][ OK ][Online ][Windows ] => Init - Update Style
[2026-03-04 19:17:00.682] [FRONTEND ][ OK ][Online ][Windows ] => Init - Engine
[2026-03-04 19:17:00.683] [FRONTEND ][INFO][Success][Windows ] => Graphic API: OpenGL
[2026-03-04 19:17:00.683] [CORE ][ OK ][Success][Libman ] => Loaded OPENGL: 2, caller: OPENGL
[2026-03-04 19:17:00.696] [FRONTEND ][ OK ][Online ][Windows ] => Init - Font
[2026-03-04 19:17:00.708] [FRONTEND ][ OK ][Online ][Windows ] => Init - Background
[2026-03-04 19:17:00.752] [FRONTEND ][ OK ][Online ][Windows ] => Init - Theme
[2026-03-04 19:17:00.753] [FRONTEND ][ OK ][Online ][Windows ] => Theme File Loaded: Gouki
[2026-03-04 19:17:00.780] [FRONTEND ][ OK ][Online ][Windows ] => Init - Done
[2026-03-04 19:17:07.062] [CORE ][ OK ][Success][Libman ] => UnLoaded OPENGL: 1, caller: OPENGL
[2026-03-04 19:17:07.068] [FRONTEND ][EVNT][Success][Windows ] => Shutdown Frontend Luncher
[2026-03-04 19:17:07.069] [FRONTEND ][EVNT][Success][Windows ] => UI - Reload
[2026-03-04 19:17:07.069] [FRONTEND ][EVNT][Waiting][Windows ] => Creating UI
[2026-03-04 19:17:07.069] [FRONTEND ][ OK ][Online ][Windows ] => Init - Create Window
[2026-03-04 19:17:07.074] [FRONTEND ][ OK ][Online ][Windows ] => Init - Update Style
[2026-03-04 19:17:07.074] [FRONTEND ][ OK ][Online ][Windows ] => Init - Engine
[2026-03-04 19:17:07.075] [FRONTEND ][INFO][Success][Windows ] => Graphic API: DirectX 11
[2026-03-04 19:17:07.075] [CORE ][ OK ][Success][Libman ] => Loaded D3D11: 2, caller: D3D11
[2026-03-04 19:17:07.130] [FRONTEND ][ OK ][Online ][Windows ] => Init - Font
[2026-03-04 19:17:07.142] [FRONTEND ][ OK ][Online ][Windows ] => Init - Background
[2026-03-04 19:17:07.202] [FRONTEND ][ OK ][Online ][Windows ] => Init - Theme
[2026-03-04 19:17:07.202] [FRONTEND ][ OK ][Online ][Windows ] => Theme File Loaded: Gouki
[2026-03-04 19:17:07.218] [FRONTEND ][ OK ][Online ][Windows ] => Init - Done
[2026-03-04 19:17:14.756] [FRONTEND ][EVNT][Waiting][Windows ] => Requested to terminate
[2026-03-04 19:17:14.757] [CORE ][ OK ][Success][Libman ] => UnLoaded D3D11: 1, caller: D3D11

Comments
Post a Comment