在macOS上用GLFW和Metal创建窗口时出现双窗口及报错问题
macOS下GLFW+Metal创建窗口出现双窗口及拖动报错的解决方法
问题现象
在macOS上使用GLFW和Metal创建窗口时,会出现两个重叠窗口,拖动窗口时抛出以下错误:
CLIENT ERROR: TUINSRemoteViewController does not override -viewServiceDidTerminateWithError: and thus cannot react to catastrophic errors beyond logging them
问题原因
- 双窗口问题:GLFW创建窗口时已默认生成带内容视图的窗口,直接替换
contentView.layer会触发Cocoa额外创建视图层,最终导致重叠窗口。 - 拖动报错:未正确绑定Metal层与GLFW窗口的生命周期,且缺少必要的视图层配置,引发窗口交互时的视图服务异常。
修复步骤
- 不直接替换GLFW窗口的
contentView.layer,改为将Metal层作为子层添加到现有视图层中,保留GLFW原有窗口结构。 - 在主循环中同步Metal层的
frame和drawableSize与窗口尺寸,避免布局异常。 - 先启用视图层再配置Metal层,确保Cocoa视图层级正确初始化。
修改后的代码
mtl_engine.mm
#include "mtl_engine.hpp" void MTLEngine::init() { initDevice(); initWindow(); } void MTLEngine::run() { while (!glfwWindowShouldClose(glfwWindow)) { // 同步Metal层尺寸与窗口 NSRect frame = metalWindow.contentView.bounds; metalLayer.frame = frame; metalLayer.drawableSize = CGSizeMake(frame.size.width * metalWindow.backingScaleFactor, frame.size.height * metalWindow.backingScaleFactor); glfwPollEvents(); } } void MTLEngine::cleanup() { [metalLayer removeFromSuperlayer]; metalLayer = nil; glfwTerminate(); metalDevice->release(); } void MTLEngine::initDevice() { metalDevice = MTL::CreateSystemDefaultDevice(); } void MTLEngine::initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindow = glfwCreateWindow(800, 600, "Metal Engine", NULL, NULL); if (!glfwWindow) { glfwTerminate(); exit(EXIT_FAILURE); } metalWindow = glfwGetCocoaWindow(glfwWindow); metalWindow.contentView.wantsLayer = YES; // 先启用视图层 metalLayer = [CAMetalLayer layer]; metalLayer.device = (__bridge id<MTLDevice>)metalDevice; metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; // 将Metal层添加为现有视图层的子层,而非替换 [metalWindow.contentView.layer addSublayer:metalLayer]; }
mtl_engine.hpp(保持原代码)
#pragma once #define GLFW_INCLUDE_NONE #import <GLFW/glfw3.h> #define GLFW_EXPOSE_NATIVE_COCOA #import <GLFW/glfw3native.h> #include <Metal/Metal.hpp> #include <Metal/Metal.h> #include <QuartzCore/CAMetalLayer.hpp> #include <QuartzCore/CAMetalLayer.h> #include <QuartzCore/QuartzCore.hpp> class MTLEngine { public: void init(); void run(); void cleanup(); private: void initDevice(); void initWindow(); MTL::Device* metalDevice; GLFWwindow* glfwWindow; NSWindow* metalWindow; CAMetalLayer* metalLayer; };
main.mm(保持原代码)
#include "mtl_engine.hpp" int main() { MTLEngine engine; engine.init(); engine.run(); engine.cleanup(); return 0; }
mtl_implementation.cpp(保持原代码)
#define NS_PRIVATE_IMPLEMENTATION #define CA_PRIVATE_IMPLEMENTATION #define MTL_PRIVATE_IMPLEMENTATION #include <Foundation/Foundation.hpp> #include <Metal/Metal.hpp> #include <QuartzCore/QuartzCore.hpp>
额外说明
- 添加子层而非替换原有层,能避免破坏GLFW默认的视图层级,从根源解决双窗口问题。
- 主循环中同步尺寸,确保窗口大小变化时Metal层能正确适配,避免后续渲染异常。
- 清理阶段移除Metal层,可避免内存泄漏问题。
内容的提问来源于stack exchange,提问作者Cedar Tree
相关产品推荐
相关产品推荐

