И подумал что возможно неплохо было бы оформить цикл обзорных статей. Пока моя тема - это Render API. В рамках рассмотрения я пытаюсь выяснить, что входит в понятия:
• Texture
• Material
• Mesh
• Render-state
• Render-pass
• Technique
• Shader
• Camera
1. CatMother Engine
1.1 Texture
1.1.1 Обзор
Сущность Texture реализована с использованием двух абстрактных интерфейсов, определяющих следующие основные моменты:
• Подсчет ссылок
• Возможность получения ссылки на интерфейс более конкретной реализации.
От BaseTexture наследуются два других интерфейса, это CubeTexture и обычная текстура Texture.
В каждом из этих интерфейсов введены методы блокировки данных и возврата нетипизированного указателя на данные, а также методы возврата габаритов и формата данных.
Отличия этих двух интерфейсов в том, что обычная текстура может быть использована вместе со специальным объектом как приемник результата растеризации некоторой сцены.
CubeTexture состоит из шести частей, как и положено. Может быть сформирована через объект Surface, который, в свою очередь, может быть загружен как готовый ресурс, либо создан из обычной текстуры.
К общим частям Texture и CubeTexture относится наличие методов создания mipmaps. Они либо вычисляются внутренними средствами реализации, либо задаются извне.
Примечательным является наличие методов textureMemoryUsed и Load/Unload. Первый, предположительно, может применяться для эвристических оценок при run-time оптимизациях. Вторые два должны обеспечить явную загрузку и выгрузку данных из GPU.
1.1.2 Иерархия классов
Основной интерфейс – это BaseTexture, он объявляет методы для подсчета ссылок. Интерфейс специализируется в Texture и CubeTexture. Эти интерфейсы очень похожи. Они определяют методы для загрузки и работы с текстурой на низком уровне (lock/unlock/data). Загрузка происходит через объект Surface, представляющий собой обертку над битовой картой определенного формата.
BaseTexture связан с BaseTextureImplInterface через свой виртуальный метод. Это сделано единственно для того, чтобы было возможно преобразование BaseTexture Dx9Texture (или в любую другую реализацию текстуры) с применением static_cast и одного виртуального вызова. Именно по этой причине заведены интерфейсы, которые содержат только пустой виртуальный деструктор: BaseTextureImplInterface и DX9BaseTextureImplInterface.
1.1.3 Выводы
Приведенная структура создана для поддержки Multi-API. Тем не менее, даже по реализации видно, что можно легко обойтись обычным static_cast и исключить множественное наследование.
1.2 Материал
1.2.1 Обзор
Для материалов есть базовый класс Material, который кроме прочего предусматривает подсчет ссылок и самоклонирование. По сути, материал является агрегатором почти всех возможных рендер-состояний, касающихся части ffp:
• Depth-тест
• Blend-режим
• Режим отсечения граней
• Stencil-тест
Кроме того, материал предусматривает работу с текстурами и установку режима смешения на каждой из стадий:
• Источник вершинного цвета diffuse/specular/ambient;
• Аргумент для TexEnvi;
• Вид операции TexEnvi;
• Источник текстурных координат (либо явно заданные, либо полученные в результате различных преобразований вершинных атрибутов, например, позиция вершин, приведенная к пространству камеры);
• Режим фильтрации;
• Режим обработки координат (wrapping).
Есть еще методы для запрета/разрешения освещения и установки флага Z-сортировки полигонов. Также материал обеспечивает настройку формата вершин.
Как только материал будет настроен, его можно будет применять к примитивам (поверхностям). Предусмотрена также сервисная функция, определяющая совместимость полученного материала с используемым устройством вывода.
Использование материала сводится к вызову трех функций. Первые две – это begin и end, подразумевающие установку и откат состояний, begin возвращает требуемое число проходов, которое использует материал. Третья функция – apply, вызывается для инициализации очередного прохода.
1.2.2 Иерархия классов
Dx9RenderingState – структура, в которой хранятся настройки материала. При вызове apply, Dx9Material передает эту структуру объекту, ответственному за коммуникацию с GAPI (Dx9GraphicsDevice), а последний уже производит установку состояний.
1.2.3 Концептуальная схема
Настройки (setup)
Из режимов (Modes) предусмотрен только режим сортировки полигонов по глубине. Под режимом понимается требуемый режим действий надсистемы. Так, например, материал не занимается сортировкой полигонов, а только может указывать на необходимость выполнения сортировки.
Любое использование текстур возможно только через материалы. В коде устанавливается константа – максимальное количество текстурных слоев, которое может содержаться в любом материале. Под текстурным слоем понимается совокупность ссылки на текстуру (BaseTexture) и настроек текстурной стадии (см. 1.2.1).
Остальные настройки (States) соответствуют конкретным рендер-состояниям (см. 1.2.1).
Второстепенные методы (services)
Есть подсчет ссылок, возможность полного клонирования и проверки совместимости материала с устройством вывода.
Непосредственное использование (usage)
Флаги (Flags) реализованы как методы, возвращающие булево значение. Флаги отражают некоторые установленные состояния (States) и режимы (Modes). Таким образом, возможно формирование более оптимального порядка следования материалов, чем может дать простая сортировка по материалам.
Про Begin, End, Apply – см 1.2.1.
1.2.4 Выводы
1. Проверку на совместимость можно убрать, добавив например, метод Commit, который произведет дальнейшую настройку в зависимости от доступных возможностей устройства.
2. Методы Begin, Apply, End относятся к категории критичных, так как вызываются в цикле вывода.
3. По реализации видно, что класс материала – един на весь RenderAPI (Dx9GraphicsDevice создает экземпляры только класса Dx9Material). В то же время метод Begin требует всегда только одного прохода, вне зависимости от настроек. Это говорит о том, что класс не дописан до конца, либо имеются нестыковки на верхних уровнях.
4. Метод Apply инициирует создание структуры Dx9RenderingState и её заполнение в соответствии с настройками, которые хранятся в приватных переменных материала. Далее, по ссылке эта структура отдается в Dx9GraphicsDevice через метод setRenderState, где осуществляется процесс перевода настроек в вызовы функций Direct3D, которые уже устанавливают нужные состояния.
Если не придираться к реализации, то остаются вопросы по общей структуре.
• Во-первых, на мой взгляд, не следует устанавливать все состояния на этапе «применения» (apply) материала, но устанавливать только те, которые действительно изменились по сравнению с предыдущим материалом. Или, лучше по сравнению с предыдущим состоянием Dx9GraphicsDevice.
• Невозможно сортировать по набору текстур.
• Трудно сортировать по состояниям вообще.
• Проверку на совместимость убрать и сразу собирать такие материалы, которые совместимы с устройством.
• В Material нет ничего от programmable pipeline. Этот вопрос будет рассмотрен далее.