Skip to content

Commit e9a1095

Browse files
committed
Metal: rebase from ImGui upstream
Introduce a dedicated bufferCacheLock and synchronize on it to avoid crashes when bufferCache is replaced while being used for @synchronize(). Initialize bufferCacheLock and switch @synchronized usages to use it. Also tidy up render logic: combine vertex buffer/setOffset into a single call to avoid redundant binds, explicitly check ImTextureID against ImTextureID_Invalid (fixes cases where ImTextureID_Invalid != 0), and change the timestamp variable to double in dequeueReusableBufferOfLength. Minor formatting/changelog updates included.
1 parent fd37953 commit e9a1095

1 file changed

Lines changed: 16 additions & 11 deletions

File tree

src/imiv/external/imgui_impl_metal_imiv.mm

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
// CHANGELOG
1919
// (minor and older changes stripped away, please see git history for details)
2020
// 2026-XX-XX: Metal: Added support for multiple windows via the ImGuiPlatformIO interface.
21+
// 2026-04-14: Metal: use a dedicated bufferCacheLock to avoid crashing when bufferCache is replaced by a new object while being used for @synchronize(). (#9367)
22+
// 2026-04-03: Metal: avoid redundant vertex buffer bind in SetupRenderState. (#9343)
23+
// 2026-03-19: Fixed issue in ImGui_ImplMetal_RenderDrawData() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310)
2124
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
2225
// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplMetal_CreateFontsTexture() and ImGui_ImplMetal_DestroyFontsTexture().
2326
// 2025-02-03: Metal: Crash fix. (#8367)
@@ -95,6 +98,7 @@ @interface MetalContext : NSObject
9598
@property (nonatomic, strong) NSMutableDictionary*
9699
renderPipelineStateCache; // pipeline cache; keyed on framebuffer descriptors
97100
@property (nonatomic, strong) NSMutableArray<MetalBuffer*>* bufferCache;
101+
@property (nonatomic, strong) NSObject* bufferCacheLock;
98102
@property (nonatomic, assign) double lastBufferCachePurge;
99103
- (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length
100104
device:(id<MTLDevice>)device;
@@ -303,8 +307,9 @@ - (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length
303307

304308
[commandEncoder setRenderPipelineState:renderPipelineState];
305309

306-
[commandEncoder setVertexBuffer:vertexBuffer.buffer offset:0 atIndex:0];
307-
[commandEncoder setVertexBufferOffset:vertexBufferOffset atIndex:0];
310+
[commandEncoder setVertexBuffer:vertexBuffer.buffer
311+
offset:vertexBufferOffset
312+
atIndex:0];
308313
}
309314

310315
// Metal Render function.
@@ -428,7 +433,8 @@ ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x,
428433
// Bind texture, Draw
429434
id<MTLSamplerState> sampler_state
430435
= bd->SharedMetalContext.defaultSamplerState;
431-
if (ImTextureID tex_id = pcmd->GetTexID()) {
436+
ImTextureID tex_id = pcmd->GetTexID();
437+
if (tex_id != ImTextureID_Invalid) {
432438
id metal_object = (__bridge id)(void*)(intptr_t)(tex_id);
433439
if ([metal_object isKindOfClass:[MetalTexture class]]) {
434440
MetalTexture* backend_tex = (MetalTexture*)metal_object;
@@ -472,12 +478,10 @@ ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x,
472478

473479
MetalContext* sharedMetalContext = bd->SharedMetalContext;
474480
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
475-
dispatch_async(dispatch_get_main_queue(), ^{
476-
@synchronized(sharedMetalContext.bufferCache) {
477-
[sharedMetalContext.bufferCache addObject:vertexBuffer];
478-
[sharedMetalContext.bufferCache addObject:indexBuffer];
479-
}
480-
});
481+
@synchronized(sharedMetalContext.bufferCacheLock) {
482+
[sharedMetalContext.bufferCache addObject:vertexBuffer];
483+
[sharedMetalContext.bufferCache addObject:indexBuffer];
484+
}
481485
}];
482486
}
483487

@@ -857,6 +861,7 @@ - (instancetype)init
857861
if ((self = [super init])) {
858862
self.renderPipelineStateCache = [NSMutableDictionary dictionary];
859863
self.bufferCache = [NSMutableArray array];
864+
self.bufferCacheLock = [[NSObject alloc] init];
860865
_lastBufferCachePurge = GetMachAbsoluteTimeInSeconds();
861866
}
862867
return self;
@@ -865,9 +870,9 @@ - (instancetype)init
865870
- (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length
866871
device:(id<MTLDevice>)device
867872
{
868-
uint64_t now = GetMachAbsoluteTimeInSeconds();
873+
double now = GetMachAbsoluteTimeInSeconds();
869874

870-
@synchronized(self.bufferCache) {
875+
@synchronized(self.bufferCacheLock) {
871876
// Purge old buffers that haven't been useful for a while
872877
if (now - self.lastBufferCachePurge > 1.0) {
873878
NSMutableArray* survivors = [NSMutableArray array];

0 commit comments

Comments
 (0)