Возможно ли преобразовать подкласс QOpenGLWidget в тот, который использует Metal вместо OpenGL?

Справочная информация: у меня есть приложение 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();
}
};

0

Решение

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

Metal — это по сути API Objective-C, поэтому вам нужно будет обернуть код 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 в этом случае.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]