Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Август 25, 2020, 18:30



Название: Код "на будущее"
Отправлено: Igors от Август 25, 2020, 18:30
Добрый день

Сейчас занимаюсь портированием кода рисования на OpenGL 3.3, сделал примерно 75%, но еще много осталось. Весьма вероятно что для OSX потребуется еще портинг на "metal", возможно еще до окончания текущего. На бздошном Вындоуз, напротив, маловероятно, но в принципе возможно захотят "vulcan". В любом случае вырисовывается мрачное будущее с поддержкой кросс-платформенности "руками" :'(

Ну и как же мне планировать код рисования "в преддверии" новых портирований? Ни "metal" ни "vulcan" никогда не видел и ничего не читал. Видел ссылку (возможно дали на этом форуме) на проект что "автоматом" переводит с OpernGL 3.3 на metal. Вряд ли покатит, но обнадеживает.

Что можете порекомендовать (можно и "настоятельно")? Ну там использовать PIMPL и все такое... Или же "нужно знать задачу" и сначала почитать metal, а там видно будет (может быть)?

Спасибо


Название: Re: Код "на будущее"
Отправлено: kambala от Август 25, 2020, 19:01
для метала есть прослойка из вулкана: https://github.com/KhronosGroup/MoltenVK


Название: Re: Код "на будущее"
Отправлено: Igors от Август 27, 2020, 10:34
для метала есть прослойка из вулкана: https://github.com/KhronosGroup/MoltenVK
Спасибо конечно, но как-то мне  "не с руки". Это выходит нужно сначала переться на vulkan (который пока не нужен) а потом уж... Боюсь меня просто не хватит на 2 новых API

По поводу прослоек такого рода. Пользовался подобными, и сам пару раз лепил. Хорошее дело для "быстрого старта" но рано или поздно все равно все сводилось к "полному" ручному портингу.

Собсно вопрос/тема весьма общие, чисто "покалякать" (в чем нет ничего плохого) как "подстелить соломки" зная что скоро портинг будет. Ну ясно не разбрасывать вызовы OpenGL по всей хате, а сосредоточить их в одном или неск файлах, сделать обертки. Что еще можно сделать?

Теоретики/эрудиты, ну где же Вы ?  :)


Название: Re: Код "на будущее"
Отправлено: qtkoder777 от Август 27, 2020, 19:59
OpenGL 5.0 вышел? Что-то гуглится.


Название: Re: Код "на будущее"
Отправлено: Igors от Август 28, 2020, 12:40
OpenGL 5.0 вышел? Что-то гуглится.
Зачем за этим следить? Портирование - тупая, но неизбежная работа, она Вас сама найдет


Название: Re: Код "на будущее"
Отправлено: Racheengel от Сентябрь 01, 2020, 14:53
Вопрос:

Что еще можно сделать?

Ответ:

Ну ясно не разбрасывать вызовы OpenGL по всей хате, а сосредоточить их в одном или неск файлах, сделать обертки.

:)

Ну от себя еще добавлю, если надо будет портировать на "то не знаю что", то можно заранее попробовать в качестве "нового АПИ" сделать какой-нибудь фейк на базе чего-то уже известного, например, любой Software Renderer. Таким образом, будет уже 2 реализации - СофтвароФейк и OpenGL, плюс вырисуется "общий АПИ" и порешаются некоторые нюансы. Потом и на вулкан будет легче идти...


Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 02, 2020, 12:08
Ну от себя еще добавлю, если надо будет портировать на "то не знаю что", то можно заранее попробовать в качестве "нового АПИ" сделать какой-нибудь фейк на базе чего-то уже известного, например, любой Software Renderer. Таким образом, будет уже 2 реализации - СофтвароФейк и OpenGL, плюс вырисуется "общий АПИ" и порешаются некоторые нюансы. Потом и на вулкан будет легче идти...
А этот вариант уже был :) Потом снес, но кое-какие прокладки остались. Приложение намного старше OpenGL, сначала рендерили сами. Правда на приличных сценах скорость падала до 1-2 fps и ниже, а об AA и мечтать не могли. Когда появился OpenGL - очень обрадовались. Пусть он много чего не делал (или делал хреново), но это списывалось на молодость системы, а волшебные fps искупали все! Увы, OpenGL так и "остался молодым", напр несчастную phong transparency так и не дождались. Ну может с metal получше будет, хз

Реально проблемы возникают когда одна "engine" имеет фичи которых нет у другой. Напр soft render мог рисовать адаптивно, а OpenGL нет. Ну ясно надо лепить метод типа "Engine::CanDrawAdaptive" и потом по всему тексту.. ох и гемор :'(


Название: Re: Код "на будущее"
Отправлено: Racheengel от Сентябрь 10, 2020, 16:19
Реально проблемы возникают когда одна "engine" имеет фичи которых нет у другой. Напр soft render мог рисовать адаптивно, а OpenGL нет. Ну ясно надо лепить метод типа "Engine::CanDrawAdaptive" и потом по всему тексту.. ох и гемор :'(

Это да :( Ну, или эмулировать как получится.


Название: Re: Код "на будущее"
Отправлено: Авварон от Сентябрь 10, 2020, 16:41
Кутешники по слухам работают над подобной абстракцией в qt6. Как и вы наступили на грабли "опенгл хватит всем"


Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 11, 2020, 13:13
Ну почему "грабли"? Это нормальный процесс эволюции.

Предлагаю поговорить об архитектуре рисования. Шансов на успех мало - как всегда с архитектурой, но тут еще труднее, требуются познания в OpenGL, пусть минимальные. Но все же попробуем. Собсно процесс рисования в OpenGL выглядит так:

1) "забиндить" vao и активный шейдер. При этом какой шейдер сейчас должен юзаться - нужно долго разбираться  в зависимости от того что хотим рисовать

2) Передать данные (индексы, вертексы, нормали, цвет, текстуры и.т.п) также забиндив их. Ну и конечно установить матрицы (столь любимые нами)

3) Установить окно/контекст и вызвать команду рисования, их немного (обычно glDrawElements или glDrawArrays).

4) Культурно "отбиндить" все, иначе нарвемся на неприятности

Все это кажется очень простым, если бы не "мелочи" или "детали". Одна команда рисования выводит примитивы одного типа (напр только тр-ки), а рисуемый объект может иметь mixed геометрию (напр и точки, и тр-ки и..). Многие (но не все) рисуемые имеют "материал" (фундаментальное понятие). Напр char обычно имеет разные текстуры для головы, рук, body и т.п. Также рисовать требуется всяко-разно - c текстурами, без, wireframe, силуэт и.т.п

Ну ладно, первый шаг очевиден: нужен класс "Engine" который "инкапсулирует" (мы знаем вумные слова) работу с OpenGL - а в перспективе и с др "back-end'ом' (правильно?). Ну попробуем что-то написать

Код
C++ (Qt)
void MyWindow::DrawObject( Object * obj,           // рисуемый объект (напр сфера)
                  TDrawStyle style )   // стиль рисования (напр phong)
{
 m_engine->SetupShaders(...);   // установили нужный шейдер
 
// установка данных
 switch (style) {
   case style_Phong:
 
// устанавливаем текстуры
     auto maps = obj->GetMaps2Draw();
     for (const auto & m : maps)
      m_engine->AddTexture(m);
 
// устанавливаем вертексы
      m_engine->SetVertices(obj->GetVertices());
 
// устанавливаем нормали
...
// устанавливаем ..
...
     break;
 }
 m_engine->Draw();     // рисуем
 m_engine->ReleaseShaders();   // культурно уходим
}
Код установки занимает много страниц, и, вероятно, будет еще расти. Др моменты (SetupShaders и Draw) здесь не показаны, но, как мы знаем, там тоже все не так просто. Также заметно что хотя OpenGL в явном виде здесь нет - многое все равно на него завязано.

Вопрос: достаточно ли такого взаимодействия Window <-> Engine или нужны еще классы? Если да то какие, и что они должны делать?

Спасибо




Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 11, 2020, 14:57
Ну ответы вряд ли последуют, здесь реально трудно (хотя буду рад ошибиться). Попробую чуть продолжить. Что плохо в приведенном псевдокоде?

Плохо что это "god-method" (хотя слышал только gpd-class). Метод неприятно велик и использовать его повторно не удается. Да, он обеспечивает рисование 3D объектов, но, помимо них, надо еще много чего рисовать (оси, стрелки, сетки, регионы всякие и.т.п). Как-то сделать базовый класс и подсунуть его первым аргументом - захлебнемся в if'ах на установке. Приходится выписывать ф-ции рисования и для всего остального. А поскольку требуются все новые и новые отрисовки - это становится проблемой. Я постоянно вынужден писать/переписывать разнообразные Draw


Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 12, 2020, 10:28
Продолжу монолог :)

Вообще если все статычно, то все решается, ну рано или поздно я заткну все дырки в SetupShaders и Draw и получу рабочий вариант. Но дело в том что постоянно требуются новые фичи, и моя архитектура оказывается к ней совершенно не приспособленной. Вот этим летом опять получил по дюнделю - потребовался "instanced" render, т.е. некоторые объекты нужно выводить N раз в одной команде рисования с различными трансформами (матрицами). Мало того что пришлось все шейдеры переписать (с железяки что возьмешь), но и плюсовый код развалился. Ну кое-как собрал, теперь готовлюсь получить на metal  :)

Что же я делаю не так, и как надо "правельно"? А заниматься этим никто не хочет. Ну оно и понятно, ведь жрать справочник и обогащать свой багаж знаний куда легче и приятнее. Наверно мне надо было больше "ходить с карандашом и блокнотом" - а то сделал не подумавши, и вот ... Или "завязаться на С++ 17" - оно все решит! Или вообще "изменить архитектуру своего приложения". Умных советов масса (да еще каких!), это я бестолковый  :)


Название: Re: Код "на будущее"
Отправлено: Racheengel от Сентябрь 14, 2020, 09:54
Вам нужно постараться выделить абстрактный уровень, на котором команды отрисовки выглядят вроде MyBigGreenSphere.draw(IDrawContext &dc);
И сделать имплементацию собственно "движка" вроде:

class COpenGLContext: public IDrawContext;
...
class CVulkanContext: public IDrawContext;
...
class CSomeTestBullshitContext: public IDrawContext;
...

Далее каждая (в зависимости от "движка") имплементация MyBigGreenSphere будет реализовать свои методы draw и свои приватные данные.

Получим что то вроде

Код:
class COpenGlMyBigGreenSphere : public MyBigGreenSphere 
{
public:
virtual void draw(COpenGLContext &dc)
{
// специфичный для OpenGL код отрисовки
}
private:
// специфичные для OpenGL методы и данные
...
};

и так для каждого "двига". "god-method" в таком случае распределится между классами объектов.

"По моему так" (с) Винни Пух


Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 14, 2020, 13:37
Вам нужно постараться выделить абстрактный уровень, на котором команды отрисовки выглядят вроде MyBigGreenSphere.draw(IDrawContext &dc);
И сделать имплементацию собственно "движка" вроде:
Это уже сделано, вернее "так всегда и было", упомянутый m_engine - указатель на абстрактный (базовый) класс.

Код:
class COpenGlMyBigGreenSphere : public MyBigGreenSphere 
{
public:
virtual void draw(COpenGLContext &dc)
{
// специфичный для OpenGL код отрисовки
}
private:
// специфичные для OpenGL методы и данные
...
};
Тут я не понял. Зачем рисуемому объекту COpenGlMyBigGreenSphere иметь методы специфичные для OpenGL? Напротив, нужно стремиться максимально юзать базовый IDrawContext

Собсно сейчас реализована классическая схема: есть "рисовальщик" (Engine или IDrawContext), в принципе тот же QPainter. Он пассивен, что ему сказали - то и рисует (если умеет). Поэтому вся нагрузка ложится на рисующих, тех кто скармливает ему данные рисования. Но, в отличие от QPainter, Engine интенсивно обновляется (даже при одном OpenGL движке). Конечно любое изменение ведет к немедленному обрушению кода зарядки данных и управления рисовальщиком. Это как рисовать гуй с постоянно изменяемым QPainter. Грядущее портирование - по сути та же проблема, только в большем объеме.


Название: Re: Код "на будущее"
Отправлено: Racheengel от Сентябрь 15, 2020, 14:06
Цитировать
Зачем рисуемому объекту COpenGlMyBigGreenSphere иметь методы специфичные для OpenGL?

Ну потому что эта имплементация рисования конкретного типа объектов может иметь специфичные приватные методы и данные.
И чтобы вот это всё не расползалось по "рисовальщику" и не превращало его в God-Object, можно "взять и поделить".
Вместо того, чтобы каждый раз допиливать "рисовальщик" и молиться, что всё будет отрисовываться правильно после этого, можно менять только точечно - тогда риск что-либо сломать намного меньше.


Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 16, 2020, 12:24
Ну потому что эта имплементация рисования конкретного типа объектов может иметь специфичные приватные методы и данные.
И чтобы вот это всё не расползалось по "рисовальщику" и не превращало его в God-Object, можно "взять и поделить".
Вместо того, чтобы каждый раз допиливать "рисовальщик" и молиться, что всё будет отрисовываться правильно после этого, можно менять только точечно - тогда риск что-либо сломать намного меньше.
Ну "рисуемых" (хорошие) десятки, и. помимо draw (собсно рисования) они имеют еще пару виртуалов для "picking" (выборка) которые также завязаны на Engine (ну как бы тоже рисование но невидимое). Только посадить все это на pimpl - уже не день (и не 2) работы.

Положительная сторона такого подхода - независимость от "старого говнокода", это как бы "все переписать" для нового API. Не раз замечал что многие тянутся именно к этому решению, хотя по моим наблюдениям оно никогда не было хорошим, а часто и нереально.

Ну ладно, во всяком случае есть какое-то предложение, в такой ситуации это уже хорошо. Какие еще есть подходы?


Название: Re: Код "на будущее"
Отправлено: alex312 от Сентябрь 17, 2020, 15:44
Вижу несколько вариантов (хотя я и не настоящий сварщик):

1 - Qt6  - там обещают поддержку унифицированного 3D для всех поддерживаемых платформ.
2 - Angle - https://github.com/google/angle
3 - порыться в готовых игровых движках и подобрать оптимальный.


Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 18, 2020, 09:47
Вижу несколько вариантов (хотя я и не настоящий сварщик):

1 - Qt6  - там обещают поддержку унифицированного 3D для всех поддерживаемых платформ.
2 - Angle - https://github.com/google/angle
Интересен подход: какое-то там обсуждение классов, архитектуры - неее, нафиг надо. Сразу хобот в "новые технологии"!  :) Ну смысл в этом конечно есть, все равно придется менять QOpenGLWindow, QOpenGLContext на что-то иное (пока неясно что). Про Qtб ничего не слышал, Angle, насколько мне известно, пока тоже только обещает. 

3 - порыться в готовых игровых движках и подобрать оптимальный.
А Вы сами рылись? Я время от времени пытаюсь (по др поводам/вопросам), но крайне редко (только пару раз) удачно. Примерно тем же кончаются походы в исходники Blender и.т.п. А у Вас? Поделитесь опытом


Название: Re: Код "на будущее"
Отправлено: alex312 от Сентябрь 18, 2020, 10:34
А Вы сами рылись?
Как я и написал выше, я не настоящий сварщик.
Поэтому никаких "конкретных" советов дать не могу, кроме очевидного - надо пользоваться наработками других.
Потому как проект уровня транслятора OpenGL в вулканы/металы/директиксы в одно лицо никак не получиться.

И да, Qt6 пока альфа, и поддержку платформ на уровне Qt5 они обещают к Qt6.1 (через год ?) , в angle пока нет металла (но они начали реализовывать). Но тема же называется "Код "на будущее"" :)


Название: Re: Код "на будущее"
Отправлено: Igors от Сентябрь 18, 2020, 11:02
- надо пользоваться наработками других.
Потому как проект уровня транслятора OpenGL в вулканы/металы/директиксы в одно лицо никак не получиться.
Не знаю конечно, но подозреваю что отделаться "транслятором" (т.е. обойтись косметикой сохраняя имеющийся код который вумный транслятор во что-то перегонит автоматом) здесь вряд ли удастся.