将QOpenGLWidget子类转换为使用Metal而不是OpenGL的子类是否可行?

时间:2018-10-24 21:30:13

标签: c++ macos qt opengl metal

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

一切正常,但是Apple最近deprecated OpenGL希望所有开发人员将其应用程序转换为Apple自己的“ Metal” API;暗示最终所有使用OpenGL的程序都将停止在MacOS / X上运行。

如果需要的话,我不介意在代码中进行一点#ifdef-魔术操作来支持单独的MacOS / X API,但是尚不清楚是否可以对Metal进行编码目前在Qt中。如果可以在Metal-inside-Qt中使用,什么是正确的方法?如果不是,我是否应该等待具有更好的Metal支持(例如Qt 5.12?)的Qt的将来版本,而不是浪费时间试图使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();
   }
};

1 个答案:

答案 0 :(得分:2)

是的,可以做您想做的事。由于您发布的代码使用了非常老的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容易编写和维护。