【问题标题】:Is it feasible to convert a QOpenGLWidget subclass into one that uses Metal instead of OpenGL?将 QOpenGLWidget 子类转换为使用 Metal 而不是 OpenGL 的子类是否可行?
【发布时间】:2026-01-19 05:10:02
【问题描述】:

背景:我有一个 Qt/C++ 应用程序,目前在 MacOS/X、Windows 和 Linux 上运行(并部署在)上。在应用程序的一个窗口中是几十个音频表的视图,需要经常更新(即 20Hz 或更快),所以我使用QOpenGLWidget 和一些简单的 OpenGL 例程(参见下面的示例代码)实现了该视图。

这一切都很好,但是 Apple 最近 deprecated OpenGL 并希望所有开发人员将他们的应用程序转换为 Apple 自己的“Metal”API;这意味着最终任何使用 OpenGL 的程序都将停止在 MacOS/X 上运行。

我不介意在我的代码中做一点 #ifdef-magic 来支持 MacOS/X 的单独 API,如果必须的话,但是不清楚是否可以在 Qt 中实际完成对 Metal 的编码现在。如果 Metal-inside-Qt 是可能的,那么正确的使用方法是什么?如果没有,我是否应该等待具有更好 Metal 支持的 Qt 未来版本(例如Qt 5.12?)而不是浪费我的时间试图让 Metal 在我当前的 Qt 版本(5.11.2)中工作?

// OpenGL meters view implementation (simplified for readability)
class GLMetersCanvas : public QOpenGLWidget
{  
public:
   GLMetersCanvas( [...] );

   virtual void initializeGL()
   {  
      glDisable(GL_TEXTURE_2D);
      glDisable(GL_DEPTH_TEST);
      glDisable(GL_COLOR_MATERIAL);
      glDisable(GL_LIGHTING);
      glClearColor(0, 0, 0, 0);
   }

   virtual void resizeGL(int w, int h)
   {  
      glViewport(0, 0, w, h);
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      glOrtho(0, w, 0, h, -1, 1);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
   }

   virtual void paintGL()
   {  
      const float meterWidth = [...];
      const float top        = [...];

      glClear(GL_COLOR_BUFFER_BIT);
      glBegin(GL_QUADS);

      float x = 0.0f;
      for (int i=0; i<numMeters; i++)
      {
         const float y = _meterHeight[i];

         glColor3f(_meterColorRed[i], _meterColorGreen[i], _meterColorBlue[i]);
         glVertex2f(x,            top);
         glVertex2f(x+meterWidth, top);
         glVertex2f(x+meterWidth, y);
         glVertex2f(x,            y);

         x += meterWidth;
      }
      glEnd();
   }
};

【问题讨论】:

    标签: c++ macos qt opengl metal


    【解决方案1】:

    是的,您可以随心所欲。由于您发布的代码使用了非常旧的 OpenGL 已弃用功能,因此这可能不是一个简单的过渡。此外,您最好只使用 CoreGraphics 进行简单的绘图。 (看起来像是在绘制许多纯色的四边形。这在 CoreGraphics 中非常简单且相当有效。)对于这项工作,金属似乎是矫枉过正。也就是说,这里有一些想法。

    Metal 本质上是一个 Objective-C API,因此您需要将 Metal 代码包装在某种包装器中。有多种方法可以编写这样的包装器。您可以创建一个 Objective-C 类来完成您的绘图并从您的 C++/Qt 类中调用它。 (您需要将 Qt 类放入 .mm 文件中,以便编译器将其视为 Objective-C++ 来调用 Objective-C 代码。)或者您可以使 Qt 类成为具有指向做真正工作的类。在 Windows 和 Linux 上,它可以指向执行 OpenGL 绘图的对象。在 macOS 上,它将指向使用 Metal 进行绘图的 Objective-C++ 类。

    This example of mixing OpenGL and Metal 可能有助于了解两者的相似之处和不同之处。与在 OpenGL 中设置状态和进行绘图调用的上下文不同,在 Metal 中,您使用绘图命令创建一个命令缓冲区,然后将它们提交以进行绘制。与更现代的 OpenGL 编程一样,您拥有顶点数组并将顶点和片段着色器应用于每个几何体,在 Metal 中,您还将提交顶点并使用片段和顶点着色器进行绘图。

    不过,老实说,这听起来需要做很多工作。 (但当然可以这样做。)如果你在 CoreGraphics 中这样做,它看起来像这样:

       virtual void paintCG()
       {  
          const float meterWidth = [...];
          const float top        = [...];
    
          CGRect backgroundRect = CGRectMake(...);
          CGContextClearRect(ctx, backgroundRect);
    
          float x = 0.0f;
          for (int i=0; i<numMeters; i++)
          {
             const float y = _meterHeight[i];
    
             CGContextSetRGBFillColor(ctx, _meterColorRed[i], _meterColorGreen[i], _meterColorBlue[i]);
             CGRect meterRect = CGRectMake(x, y, meterWidth, _meterHeight[i]);
             CGContextFillRect(ctx, meterRect);
    
             x += meterWidth;
          }
          glEnd();
       }
    

    它只需要你有一个CGContextRef,我相信你可以从你正在绘制的任何窗口中获得它。如果窗口是NSWindow,那么你可以调用:

     NSGraphicsContext* nsContext = [window graphicsContext];
     CGContextRef ctx = nsContext.CGContext;
    

    在这种情况下,这似乎比使用 Metal 更容易编写和维护。

    【讨论】:

      最近更新 更多