Возможно ли преобразовать подкласс QOpenGLWidget в тот, который использует Metal вместо OpenGL? - PullRequest
0 голосов
/ 25 октября 2018

Справочная информация: у меня есть приложение Qt / C ++, которое в настоящее время работает (и разворачивается) на MacOS / X, Windows и Linux.В одном из окон приложения отображается несколько десятков аудиометров, которые необходимо часто обновлять (т. Е. С частотой 20 Гц или выше), поэтому я реализовал это представление с помощью QOpenGLWidget и некоторых простых подпрограмм OpenGL (см. Пример коданиже).

Все это прекрасно работает, однако Apple недавно устарела в OpenGL и хочет, чтобы все разработчики вместо этого конвертировали свои приложения в собственный API Apple «Металл»;с учетом того, что в конечном итоге любая программа, использующая OpenGL, перестанет работать на MacOS / X.

Я не возражаю против использования немного #ifdef -магий внутри моего кода для поддержки отдельного API для MacOS / Xесли я должен, однако, не ясно, является ли кодирование в Metal тем, что на самом деле можно сделать в Qt в настоящее время.Если Metal-inside-Qt возможен, какой правильный подход использовать?Если нет, я должен ждать будущего выпуска Qt с лучшей поддержкой Metal (например, 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();
   }
};

1 Ответ

0 голосов
/ 27 октября 2018

Да, можно делать то, что вы хотите.Это, вероятно, не будет простым переходом из-за того, что код, который вы разместили, использует очень старые устаревшие функции OpenGL.Кроме того, вам может быть лучше использовать CoreGraphics для простого рисования, которое вы делаете.(Похоже, нарисовано несколько сплошных квадратов. Это очень просто и достаточно эффективно в CoreGraphics.) Металл кажется излишним для этой работы.Тем не менее, вот несколько идей.

Metal - это по сути Objective-C API, поэтому вам нужно будет обернуть код Metal в какую-то оболочку.Есть несколько способов написать такую ​​обертку.Вы можете создать класс Objective-C, который будет рисовать, и вызвать его из своего класса C ++ / Qt.(Вам нужно поместить ваш класс Qt в файл .mm, чтобы компилятор обрабатывал его как Objective-C ++ для вызова кода Objective-C.) Или вы можете сделать свой класс Qt абстрактным классом, который имеет указатель реализации накласс, который делает реальную работу.В Windows и Linux он может указывать на объект, который выполняет рисование OpenGL.В macOS это будет указывать на ваш класс Objective C ++, который использует Metal для рисования.

Этот пример смешивания OpenGL и Metal может быть информативным для понимания того, как эти 2 похожи и где они различаются,Вместо того, чтобы иметь контекст, в котором вы устанавливаете состояние и делаете вызовы рисования, как в 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 в этом случае.

...