You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

问题原因

  1. 双窗口问题:GLFW创建窗口时已默认生成带内容视图的窗口,直接替换contentView.layer会触发Cocoa额外创建视图层,最终导致重叠窗口。
  2. 拖动报错:未正确绑定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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:17:45