I'm trying to create the most basic example program using ImGui, GLFW and the Metal API.
This is my code:
// main.mm
#include <iostream>
#include <GLFW/glfw3.h>
#define GLFW_EXPOSE_NATIVE_COCOA
#include <GLFW/glfw3native.h>
#include <imgui.h>
#include <backends/imgui_impl_osx.h>
#include <backends/imgui_impl_metal.h>
#include <Metal/Metal.h>
#include <QuartzCore/CAMetalLayer.h>
int main() {
if (!glfwInit()) {
std::cout << "Failed to init GLFW\n";
return 1;
}
auto* W = glfwCreateWindow(1000, 800, "My Window", nullptr, nullptr);
if (!W) {
std::cout << "Failed to create window\n";
return 1;
}
int width = 0, height = 0;
glfwGetWindowSize(W, &width, &height);
ImGui::CreateContext();
NSWindow* NSW = glfwGetCocoaWindow(W);
ImGui_ImplOSX_Init(NSW.contentView);
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
ImGui_ImplMetal_Init(device);
CAMetalLayer* layer = [[CAMetalLayer alloc] init];
layer.device = device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
layer.drawableSize = CGSize{ 2.0 * width, 2.0 * height };
NSW.contentView.wantsLayer = true;
[NSW.contentView setLayer:layer];
id commandQueue = [device newCommandQueue];
ImGui_ImplMetal_CreateFontsTexture(device);
while (!glfwWindowShouldClose(W)) {
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2(width, height);
ImGui_ImplOSX_NewFrame(NSW.contentView);
ImGui::NewFrame();
// User code
ImGui::ShowDemoWindow();
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
MTLRenderPassDescriptor* renderPassDescriptor = [[MTLRenderPassDescriptor alloc] init];
MTLRenderPassColorAttachmentDescriptor* caDesc = [[MTLRenderPassColorAttachmentDescriptor alloc] init];
id<CAMetalDrawable> drawable = layer.nextDrawable;
caDesc.texture = drawable.texture;
caDesc.loadAction = MTLLoadActionClear;
caDesc.storeAction = MTLStoreActionStore;
[renderPassDescriptor.colorAttachments setObject: caDesc
atIndexedSubscript: 0];
ImGui_ImplMetal_NewFrame(renderPassDescriptor);
// Rendering
ImGui::Render();
ImDrawData* drawData = ImGui::GetDrawData();
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1, 0, 1, 1);
id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder pushDebugGroup:@"Dear ImGui rendering"];
ImGui_ImplMetal_RenderDrawData(drawData, commandBuffer, renderEncoder);
[renderEncoder popDebugGroup];
[renderEncoder endEncoding];
// Present
[commandBuffer presentDrawable: drawable];
[commandBuffer commit];
[drawable present];
glfwPollEvents();
}
ImGui_ImplMetal_Shutdown();
ImGui_ImplOSX_Shutdown();
}
This creates a window that I can interact with. But that's about it. The window is black, the program leaks memory like crazy (after 3 seconds it uses 3GB of memory), it draws between 300 and 3000 frames per second and crashes after a few more seconds in a function called
layer_private_present_impl(_CAMetalLayerPrivate*, CAMetalDrawable*, double, unsigned int)
What am I doing wrong?
There are two problems in your case:
The code is written in a way that assumes that Automatic Reference Counting (ARC) is enabled, but it's not enabled in the project settings, at least in the link you attached in the comment.
You are missing @autoreleasepool { ... }
around the body of the while (!glfwWindowShouldClose(W))
. If you add autoreleasepool, it will work, kinda.
Just to be clear, to fix this example, you don't need to do 1, just add an autoreleasepool.
But you really need to enable ARC. You can do so by going into project settings and enabling Objective-C Automatic Reference Counting
or by enabling this Xcode setting CLANG_ENABLE_OBJC_ARC = NO
. Or you can also pass -fobjc-arc
compiler option.