我正在尝试使用 ImGui、GLFW 和 Metal API 创建最基本的示例程序。
这是我的代码:
// 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();
}
这将创建一个我可以与之交互的窗口。但仅此而已。窗口是黑色的,程序疯狂地泄漏内存(3秒后它使用了3GB内存),它每秒绘制300到3000帧,并在一个名为
的函数中再几秒后崩溃layer_private_present_impl(_CAMetalLayerPrivate*, CAMetalDrawable*, double, unsigned int)
我做错了什么?
您的案例有两个问题:
代码的编写方式假设启用了自动引用计数(ARC),但在项目设置中未启用它,至少在评论中附加的链接中是这样。
您在
@autoreleasepool { ... }
的身体周围缺少 while (!glfwWindowShouldClose(W))
。如果你添加 autoreleasepool,它会起作用,有点。
需要明确的是,要修复此示例,您不需要执行 1,只需添加一个自动释放池即可。
但是你确实需要启用 ARC。您可以通过进入项目设置并启用
Objective-C Automatic Reference Counting
或启用此 Xcode 设置 CLANG_ENABLE_OBJC_ARC = NO
来完成此操作。或者你也可以传递 -fobjc-arc
编译器选项。