Рендерер графического процессора что это opengl
Рендеринг 3D графики — непростое занятие, но крайне интересное и захватывающее. Эта статья для тех, кто только начинает знакомство с OpenGL или для тех кому интересно, как работают графические конвейеры, и что они из себя представляют. В этой статье не будет точных инструкций, как создать OpenGL контекст и окно или как написать своё первое оконное приложение на OpenGL. Связанно это с особенностями каждого языка программирования и выбором библиотеки или фреймворка для работы с OpenGL (Я буду использовать C++ и GLFW) тем более в сети легко найти туториал под интересующий вас язык. Все примеры, приведённые в статье, будут работать и на других языках с немного изменённой семантикой команд, почему это так, расскажу чуть позже.
Унифицированные или специализированные шейдерные ядра
В эпоху ранних мобильных видеокарт, до распространения комплексных эффектов, существовала точка зрения, что для фрагментных шейдеров достаточно поддержки вычислений на пониженной точности. Ведь в типичном режиме дисплея применяется 8, а то и меньше бит на каждый канал цвета. Такая точка зрения привела к использованию специализированных шейдерных ядер. Для вершин использовались ядра, оптимизированные для матричных преобразований на повышенной точности FP24/FP32(highp). Для пикселей — ядра, более эффективно работающие с пониженной точностью FP16 (mediump). При этом highp на них не поддерживался. На первый взгляд, такая специализация позволяет добится более рационального распределения транзисторов на чипе. Однако, на практике это приводит к трудностям при разработке комплексных эффектов, а также при использовании текстур большого разрешения. Кроме того, специализация ядер может приводить к vertex/fragment bottleneck. Таким термином называют ситуацию, когда из-за несимметричной нагрузки на вершинные и пиксельные ядра часть ядер «простаивала».
Поэтому в современных архитектурах применяются унифицированные ядра. Такие ядра могут брать на себя вершинные, пиксельные и другие вычислительные задачи в зависимости от нагрузки.
Immediate mode mobile GPUs
Мы рассмотрели наиболее распространенные семейства мобильных видеокарт. Во всех этих семействах применялась тайловая архитектура рендера. Однако существуют мобильные видеокарты, в которых используется и традиционный immediate mode подход. Приведем некоторые из них:
- nVIdia (Tegra SoC)
- Все семейство Intel, кроме последних Gen 11
- Vivante GCxxxx (+Arcturus GC8000)
PowerVR SGX
Первые видеокарты PowerVR SGX появились в далеком 2009-м году. Существует несколько поколений этой архитектуры: Series5, Series5XT и Series5XE. Apple использовала эти GPU вплоть до iPAD 4/iPhone 5/iPOD Touch 5. Можно привести такие особенности SGX:
- унифицированные шейдерные ядра
- OpenGL ES 2.0
- векторный набор инструкций
- поддержка 10-битной точности lowp в шейдерах
- низкая производительность зависимых текстурных выборок (dependent texture reads)
PowerVR от Imagination Technologies
Imagination Technologies — британская fabless компания, знаменитая разработкой GPU для продукции Apple. Эту роль компания выполняла вплоть до появления iPhone 8/X, в которых используются внутренние разработки Apple. Хотя по оставшимся без изменений рекомендациям по оптимизациям для этих чипов, а также по наличию патентных исков к Apple от Imagination можно предполагать, что Apple продолжила развивать архитектуру PowerVR — оригинальную разработку от Imagination. В начале 2020 года Apple вернулась к практике лицензирования у Imagination Technologies. Кроме устройств с iOS/iPadOS, видеокарты PowerVR устанавливаются в большое количество смартфонов и планшетов на базе Android.
Рассмотрим семейства видеокарт PowerVR, которые до сих пор можно встретить у пользователей.
Как работает OpenGL
Если смотреть с точки зрения разработчика, то OpenGL — программный интерфейс, который управляет работой видеокарты. Всего есть чуть более 150 команд, с помощью которых программист определяет, какие именно объекты будут отправлены на рендеринг.Этим же стандартом определяются более тонкие настройки: например, тройная буферизация, трассировка лучей или сглаживание в играх.
Нет необходимости создавать разные версии графических объектов для отображения в различных режимах качества графики: все подстраивается «на автомате», исходя из заданных программистов параметров.
Проще говоря, программист определяет отображаемые объекты в трехмерном пространстве, их взаимное положение и взаимодействие, масштабирование, угол обзора и т.д., а также цвет и текстуру, а OpenGL уже заботится об их рендеринге.
Можно утверждать, что этот инструмент только воспроизводит объекты, но не взаимодействует с устройствами ввода — мышью, клавиатурой, геймпадом или игровым рулем. За это, а также за работу менеджера окон, отвечают уже другие компоненты.
Несмотря на кажущуюся сложность, OpenGL имеет четко продуманную структуру и простой процедурный интерфейс. При этом с помощью этого инструмента можно создавать действительно сложные сцены, состоящие из множества компонентов. Вычислительных мощностей требуется меньше по сравнению с другими библиотеками.
Некоторые версии OpenGL поддерживают работу по сети: объекты рендерятся на сервере, а клиентское приложение получает только их отображение. Благодаря этому можно создавать мощные воспроизводящие комплексы, состоящие из множества компьютеров.
Следует отметить, что в отличие от главного конкурента OpenGL можно считать универсальным инструментом. Главный конкурент, DirectX, «заточен» именно под игры. Многие игры поддерживают обе технологии.
Нужно ли переключаться между ними? На мой взгляд, нет никакой разницы, что именно вы используете, Независимо от технологии существенной разницы в производительности не замечено, если речь идет о платформе Windows.
Если же вы используете эмулятор Андроид на ПК, в этом случае рекомендую переключиться OpenGL: для систем семейства Unix этот инструмент подходит больше.
Также рекомендую ознакомиться с публикациями «Что делать, если видеокарта не поддерживает DirectX» и «Как посмотреть параметры видеокарты». Буду признателен, если вы расшарите этот пост в социальных сетях. До скорой встречи!
Приветствую, дорогие любители и профессионалы, программисты графики! Приступаем ко второй части нашего цикла статей про оптимизацию рендера под Mobile. В этой части мы будем рассматривать основные семейства GPU, представленные у игроков на Mobile.
Первая программа
Теперь мы знаем основы работы с OpenGL. Можно написать простую программу для закрепления знаний.
Начнем с того, что нужно подключить заголовочный файл GLUT:
Теперь мы уже знаем, что писать в main. Зарегистрируем два обработчика: для рисования содержимого окна и обработки изменения его размеров. Эти два обработчика по сути используются в любой программе, использующей OpenGL и GLUT.
Теперь надо написать функцию-обработчик изменений размеров окна. Зададим область вывода изображения размером со все окно при помощи команды glViewport (х, у, ширина, высота). Затем загрузим матрицу проекции glMatrixMode (GL_PROJECTION), заменим ее единичной glLoadIdentity () и установим ортогональную проекцию. И наконец загрузим модельно-видовую матрицу glMatrixMode (GL_MODELVIEW) и заменим ее единичной.
В итоге получим:
- GL_COLOR_BUFFER_BIT — для очистки буфера цвета
- GL_DEPTH_BUFFER_BIT — для очистки буфера глубины
- GL_ACCUM_BUFFER_BIT — для очистки буфера накопления
- GL_STENCIL_BUFFER_BIT — для очистки трафаретного буфера
Все! Можно компилировать. Должно получиться что-то вроде этого:
Весь код целиком (для кто не осилил статью).
В принципе ничего сложного в этом нет, по крайней мере если вы уже сталкивались с графикой до этого.
Из неофициального
Предпринято немало попыток обратного инжиниринга видеокарт Mali с целью создания Open Source драйверов под Linux. Труды самоотверженных ребят, пытающихся это осуществить, позволяют взглянуть на недокументированные особенности видеокарт Mali. Так, в проекте PanFrost есть disassembler для Mali Midgard/Bifrost, при помощи которого можно познакомится с набором шейдерных инструкций (открытой официальной информации на эту тему нет).
Mali Bifrost
Следующая за Midgard архитектура Bifrost выделяется переходом к скалярной ISA. По сравнению с предыдущей архитектурой увеличено максимальное количество ядер (с 16 до 32), а также поддерживается улучшенный интерфейс с CPU, позволяющий осуществлять когерентный доступ к общей памяти: изменения содержимого памяти CPU/GPU сразу становятся «видны» друг другу несмотря на кэши, что позволяет упростить синхронизацию.
Объекты в OpenGL
Библиотеки OpenGL написаны на C и имеют многочисленные API к ним для разных языков, но тем не менее это C библиотеки. Множество конструкций из языка С не транслируются в высокоуровневые языки, поэтому OpenGL был разработан с использованием большого количества абстракций, одной из этих абстракций являются объекты.
Объект в OpenGL — это набор опций, который определяет его состояние. Любой объект в OpenGL можно описать его (id) и набором опций, за который он отвечает. Само собой, у каждого типа объектов свои опции и попытка настроить несуществующие опции у объекта приведёт к ошибке. В этом кроется неудобство использования OpenGL: набор опций описывается C подобной структурой идентификатором которого, зачастую, является число, что не позволяет программисту найти ошибку на этапе компиляции, т.к. ошибочный и правильный код семантически неотличимы.
С таким кодом вы будете сталкиваться очень часто, поэтому когда вы привыкнете, что это похоже на настройку конечного автомата, вам станет намного проще. Данный код лишь показывает пример того, как работает OpenGL. В последствии будут представлены реальные примеры.
Но есть и плюсы. Основная фишка этих объектов состоит в том, что мы можем объявлять множество объектов в нашем приложении, задавать их опции и когда бы мы не запускали операции с использованием состояния OpenGL мы можем просто привязать объект с нашими предпочитаемыми настройками. К примеру этом могут быть объекты с данными 3D модели или нечто, что мы хотим на этой модели отрисовать. Владение несколькими объектами позволяет просто переключаться между ними в процессе отрисовки. С таким подходом мы можем сконфигурировать множество объектов нужных для отрисовки и использовать их состояния без потери драгоценного времени между кадрами.
Чтобы начать работать с OpenGL нужно познакомиться с несколькими базовыми объектами без которых мы ничего не сможем вывести на экран. На примере этих объектов мы поймём как связывать данные и исполняемые инструкции в OpenGL.
Векторный (SIMD) или скалярный набор инструкций
В духе описанного выше стремления экономить на транзисторах, специализируя ядра, происходил и дизайн набора шейдерных инструкций. Большинство типичных преобразований для трехмерной графики оперируют 4-х компонентными векторами. Поэтому ранние GPU работали именно с такими операндами. Если же в коде шейдера содержались разнородные скалярные операции, которые не удавалось упаковать в векторные операции оптимизатором, часть вычислительных мощностей не задействовалась. Это явление можно проиллюстрировать так:
Имеется шейдер, осуществляющий распространенную операцию Multiply Add: умножить 2 операнда, а затем добавить третий. При компиляции на условной векторной архитектуре (Vector ISA = Vector Instruction Set Architecture) мы получаем одну векторную инструкцию vMADD, выполняющуюся 1 такт. На условной скалярной архитектуре мы получаем 4 скалярные инструкции, которые благодаря усовершенствованному конвейеру также выполняются за 1 такт. Теперь рассмотрим усложненный шейдер, выполняющий 2 операции, но над 2-х компонентными операндами.
В случае векторной архитектуры получаем уже 2 инструкции, требующие 2 такта на выполнение. При этом над компонентами .zw действия не производятся, и вычислительные мощности простаивают. В случае скалярной архитектуры эти же операции можно упаковать в 4 скалярных sMADD, выполняющихся за тот же 1 такт. Таким образом на скалярной архитектуре за счет усовершенствования конвейера достигается большая плотность вычислений. Тем не менее, как будет показано ниже, векторная ISA по-прежнему актуальна. А значит, есть смысл применять техники векторизации шейдерного кода. Они позволяют добиться повышенной производительности на видеокартах с векторными ISA. В то же время, как правило, это не вредит быстродействию на более современных скалярных ISA.
Опираясь на приведенные характеристики, рассмотрим распространенные в наше время семейства мобильных GPU. Начнем с наиболее часто встречающегося семейства. Многие знают, что речь идет о видеокартах Mali от британской компании ARM. Непосредственно производством чипов ARM не занимается, предлагая вместо этого интеллектуальную собственность. Как и другие мобильные видеокарты, Mali является составной частью System on Chip(SoC), т.е. работает с общей для CPU и GPU памятью и шиной.
Зависимые текстурные выборки (dependent texture reads)
Как известно, операции сэмплирования текстур являются наиболее медленными из-за необходимости ожидания результатов чтения памяти. В случае мобильных SoС речь идет об общей системной памяти с CPU. Для уменьшения количества обращений к медленной памяти используются текстурные кеши. Чтобы не было простоя в начале растеризации с использованием текстуры, есть смысл закешировать используемые участки заранее. Если фрагментный шейдер использует текстурную координату, передаваемую из вершинного шейдера без изменений, то необходимый для кэширования участок текстуры можно определить до выполнения фрагментного шейдера. Если же фрагментный шейдер меняет текстурную координату либо вычисляет ее, используя данные из другой текстуры, то сделать это не всегда возможно. В результате выполнение фрагментного шейдера может замедлиться. Видеокарты PowerVR SGX особенно «болезненно» реагируют на такой сценарий. При этом даже использование перестановки компонент текстурной координаты (swizzle) приводит к dependent texture read. Приведем пример shader program без dependent texture read.
vertex program
fragment program
fragment program
PowerVR Rogue
Дальнейшее развитие видеокарты PowerVR получили в архитектуре Rogue.Существует несколько поколений этой архитектуры: от Series6 до Series9. У всех PowerVR Rogue есть такие особенности:
- унифицированные шейдерные ядра
- скалярная архитектура инструкций
- поддержка OpenGL ES 3.0+ (вплоть до 3.2, а также Vulkan API у свежих линеек)
Что такое OpenGL?
OpenGL — cпецификация, определяющая платформонезависимый программный интерфейс для написания приложений, использующих двумерную и трёхмерную компьютерную графику. OpenGL не является реализацией, а только описывает те наборы инструкций, которые должны быть реализованы, т.е. является API.
Каждая версия OpenGL имеет свою спецификацию, мы будем работать начиная с версии 3.3 и до версии 4.6, т.к. все нововведения с версии 3.3 затрагивают мало значимые для нас аспекты. Перед тем как начать писать своё первое OpenGL приложение, рекомендую узнать какие версии поддерживает ваш драйвер(сделать это можно на сайте вендора вашей видеокарты) и обновить драйвер до последней версии.
PowerVR TBDR
Как и во всех распространенных мобильных GPU, в PowerVR используется тайловый конвейер. Но в отличие от конкурентов, Imagination пошла дальше и реализовала отложенную растеризацию примитивов, позволяющую пропускать шейдинг невидимых пикселей независимо от порядка отрисовки. Такой подход получил название Tile Based Deferred Rendering,а процесс устранения невидимых пикселей — Hidden Surface Removal (HSR).
Hidden Surface Removal
Рекомендуется рисовать непрозрачную геометрию до прозрачной и не использовать Z Prepass, который в случае видеокарт PowerVR в большинстве сценариев приведет к лишней работе. Однако несколько подряд идущих прозрачных пикселей, перекрывающих друг друга, полностью шейдятся для получения корректного цвета с учетом смешивания. Последний же прозрачный пиксель может быть отброшен, если за ним следует непрозрачный пиксель.
Базовые объекты: Шейдеры и шейдерные программы.=
Shader — это небольшая программа которая выполняется на графическом ускорителе(GPU) на определённом этапе графического конвейера. Если рассматривать шейдеры абстрактно, то можно сказать, что это этапы графического конвейера, которые:
- Знают откуда брать данные на обработку.
- Знают как обрабатывать входные данные.
- Знают куда записать данные для дальнейшей их обработки.
Но как же выглядит графический конвейер? Очень просто, вот так:
Пока в этой схеме нас интересует только главная вертикаль, которая начинается с Vertex Specification и заканчивается на Frame Buffer. Как уже говорилось ранее, каждый шейдер имеет свои входные и выходные параметры, которые отличаются по типу и количеству параметров.
Кратко опишем каждый этап конвейера, чтобы понимать, что он делает:
- Вершинный шейдер — нужен для обработки данных 3D координат и всех других входных параметров. Чаще всего в вершинном шейдере производятся вычисление положения вершины относительно экрана, расчёт нормалей (если это необходимо) и формирование входных данных в другие шейдеры.
- Шейдер тесселяции и шейдер контроля тесселяции — эти два шейдера отвечают за детализацию примитивов, поступающих из вершинного шейдера и подготавливают данные для обработки в геометрическом шейдере. Сложно описать в двух предложениях на что способны эти два шейдера, но чтобы у читателей было небольшое представление приведу пару изображений с низким и высоким уровнем теселяции:
Шейдеры OpenGL пишутся на специальном С-подобном языке GLSL из которого они компилируются и линкуются в шейдерную программу. Уже на данном этапе кажется, что написание шейдерной программы это крайне трудоёмкое занятие, т.к. нужно определить 5 ступеней графического конвейера и связать их воедино. К большому счастью это не так: в графическом конвейере по умолчанию определены шейдеры тесселяции и геометрии, что позволяет нам определить всего два шейдера — вершинный и фрагментный (иногда его назвают пиксельным шейдером). Лучше всего рассмотреть эти два шейдера на классическом примере:
Эти два простых шейдера ничего не вычисляют лишь передают данные дальше по конвейеру. Обратим внимение как связаны вершинный и фрагментный шейдеры: в вершинном шейдере объявлена out переменная Color в которую будет записан цвет после выполнения главной функции, в то время как в фрагментном шейдере объявлена точно такая же переменная с квалификатором in, т.е. как и описывалось раньше фрагментный шейдер получает данные из вершинного посредством нехитрого прокидывания данных дальше через конвейер (но на самом деле не всё так просто).
Замечание: Если в фрагментном шейдере не объявить и не проинициализировать out переменную типа vec4, то на экран ничего выводиться не будет.
Mali Midgard
- унифицированные шейдерные ядра
- OpenGL ES 3.2 (compute & geometry shaders, tesselation. )
Еще одной особенностью Mali Midgard является технология Forward Pixel Kill. Расчет каждого пикселя производится в отдельном потоке фрагментного ядра. Если во время выполнения потока становится известно, что результирующий пиксель будет перекрыт непрозрачным пикселем другого примитива, поток завершается преждевременно и освободившиеся ресурсы используются для других вычислений.
Что это за программа
И сразу же хочу уточнить, что это вовсе не программа в привычном понимании, не драйвер, не протокол и не служба.
OpenGL — спецификация, которой определяется программный интерфейс для написания приложений, использующих 2D и 3D графику. По сути, это инструмент, который регулирует рендеринг изображения видеокартой.
Название расшифровывается как Open Graphics Library, то есть «Открытая графическая библиотека». Открытость инструмента означает, что им может воспользоваться любой разработчик без всякой маржи и лицензионных отчислений.
Инструмент кроссплатформенный: созданные с его помощью приложения будут работать в любой среде, которые осуществляют его поддержку.
При этом работать будут одинаково все в зависимости от мощности рабочей станции. Такая реализация освобождает разработчика от необходимости писать код под каждую конкретную платформу, что позволяет полностью сосредоточиться на графической составляющей.
Основные области применения OpenGL — видеоигры, системы автоматизированного проектирования, поддержка виртуальной реальности и визуализация различных проектов. На платформе Windows главным конкурентом технологии является DirectX.
О том, что такое DirectX и зачем он нужен, вы можете почитать вот тут.
Концепция OpenGL была разработана в 1982 году в университете Стэнфорда. Аппаратно прототип технологии впервые реализовала компания Silicon Graphics, создавшая конвейер для рендеринга. Ее разработки стали основой библиотек OpenGL.
У большинства геймеров нет самых горячих телефонов.
Поскольку не у всех есть лучшие телефоны, Google нашел решение предложить небольшое повышение производительности тем, у кого слабые телефоны. Этот параметр повышает производительность игр при условии, что у нас есть ОС Oreo Android или новее. В старых версиях Android этот параметр недоступен.
Включите OpenGL Skies для лучшей производительности в Android-играх
Включите OpenGL Skia для лучшей производительности в играх и интерфейсе Android
Игры на мобильных платформах (Android и iOS) достигли невероятного роста всего за несколько лет. Если сначала игры были довольно простыми, как, например, 80 PC, эти игры стали чрезвычайно сложными.
Freedreno
В отличие от ARM и Imagination Technologies, Qualcomm неохотно делится подробностями внутреннего устройства своих GPU. Однако, благодаря усилиям «обратного инженера» Роба Кларка, многое можно узнать из проекта Freedreno, open source драйвера Adreno для Linux.
Rob Clark, автор Freedreno
Базовые объекты: Буферы и Вершинные массивы
Я думаю не стоит объяснять что такое буферные объекты, лучше рассмотрим как создать и заполнить буффер в OpenGL.
Ничего сложно в этом нет, привязываем сгенереный буффер к нужному таргету (позже узнаем к какому) и загружаем данные указывая их размер и тип использования.
GL_STATIC_DRAW — данные в буфере изменяться не будут.
GL_DYNAMIC_DRAW — данныe в буфере будут изменяться, но не часто.
GL_STREAM_DRAW — данные в буфере будут изменяться при каждом вызове отрисовки.
Отлчно, теперь в памяти GPU расположенные наши данные, скомпилирована и слинкована шейдерная программа, но остаётся один нюанс: как программа узнает откуда брать входные данные для вершинного шейдера? Данные мы загрузили, но никак не указали откуда шейдерной программе их брать. Эту задачу решает отдельный тип объектов OpenGL — вершинные массивы.
Как и с буферами вершинные массивы лучше рассмотреть на примере их конфигурации
Создание вершинных массивов ничем не отличается от создания других OpenGL объектов, самое интересное начинается после строчки: Вершинный массив (VAO) запоминает все привязки и конфигурации проводимые с ним, в том числе и привязывание буферных объектов для выгрузки данных. В данном примере такой объект всего один, но на практике их может быть несколько. После чего производится конфигурация вершинного атрибута с определённым номером:
- Номер атрибута, который мы хотим сконфигурировать.
- Количество элементов, которые мы хотим взять. (Т.к. входная переменная вершинного шейдера с layout = 0 имеет тип vec3, то мы берём 3 элемента типа float)
- Тип элементов.
- Нужно ли нормализовывать элеметы, если речь идёт о векторе.
- Смещение для следующей вершины (Т.к. у нас последовательно расположены координаты и цвета и каждый имеет тип vec3, то смещаемся на 6 * sizeof(float) = 24 байта).
- Последний аргумент показывает какое смещение брать для первой вершины. (для координат этот аргумент равен 0 байт, для цветов 12 байт)
Всё теперь мы готовы отрендерить наше первое изображение
Не забудьте привязать VAO и шейдерную программу перед вызовом отрисовки.
Если вы всё сделали правильно, то вы должны получить вот такой результат:
Результат впечатляет, но откуда в треугольнике градиентная заливка, ведь мы указали всего 3 цвета: красный, синий и зелёный для каждой отдельной вершины? Это магия шейдера растеризации: дело в том, что во фрагментный шейдер попадает не совсем то значение Color которое мы установили в вершинном. Вершин мы передаём всего 3, но фрагментов генерируется намного больше (фрагментов ровно столько же сколько закрашенных пикселей). Поэтому для каждого фрагмента берётся среднее из трёх значений Color в зависимости от того насколько близко он находится к каждой из вершин. Это очень хорошо прослеживается у углов треугольника, где фрагменты принимают то значение цвета, которое мы указали в вершинных данных.
Забегая чуть вперёд скажу, что текстурные координаты передаются точно так же, что позволяет с лёгкостью накладывать текстуры на наши примитивы.
Думаю на этом стоит закончить данную статью, самое сложное уже позади, но самое интересное только начинается. Если у вас есть вопросы или вы увидели ошибку в статье, напишите об этом в комментариях, я буду очень признателен.
В следующей статье мы рассмотрим трансформации, узнаем о unifrom переменных и научимся накладывать текстуры на примитивы.
Знакомство с OpenGL нужно начать с того, что OpenGL — это спецификация. Т.е. OpenGL лишь определяет набор обязательных возможностей. Реализация же зависит от конкретной платформы.
OpenGL является кроссплатформенным, независимым от языка программирования API для работы с графикой. OpenGL — низкоуровневый API, поэтому для работы с ним неплохо иметь некоторое представление о графике в целом и знать основы линейной алгебры.
Основы программы на OpenGL
- GLUT_RGBA — включает четырехкомпонентный цвет (используется по умолчанию)
- GLUT_RGB — то же, что и GLUT_RGBA
- GLUT_INDEX — включает индексированный цвет
- GLUT_DOUBLE — включает двойной экранный буфер
- GLUT_SINGLE — включает одиночный экранный буфер (по умолчанию)
- GLUT_DEPTH — включает Z-буфер (буфер глубины)
- GLUT_STENCIL — включает трафаретный буфер
- GLUT_ACCUM — включает буфер накопления
- GLUT_ALPHA — включает альфа-смешивание (прозрачность)
- GLUT_MULTISAMPLE — включает мультисемплинг (сглаживание)
- GLUT_STEREO — включает стерео-изображение
- void glutDisplayFunc (void (*func) (void)) — задает функцию рисования изображения
- void glutReshapeFunc (void (*func) (int width, int height)) — задает функцию обработки изменения размеров окна
- void glutVisibilityFunc (void (*func)(int state)) — задает функцию обработки изменения состояния видимости окна
- void glutKeyboardFunc (void (*func)(unsigned char key, int x, int y)) — задает функцию обработки нажатия клавиш клавиатуры (только тех, что генерируют ascii-символы)
- void glutSpecialFunc (void (*func)(int key, int x, int y)) — задает функцию обработки нажатия клавиш клавиатуры (тех, что не генерируют ascii-символы)
- void glutIdleFunc (void (*func) (void)) — задает функцию, вызываемую при отсутствии других событий
- void glutMouseFunc (void (*func) (int button, int state, int x, int y)) — задает функцию, обрабатывающую команды мыши
- void glutMotionFunc (void (*func)(int x, int y)) — задает функцию, обрабатывающую движение курсора мыши, когда зажата какая-либо кнопка мыши
- void glutPassiveMotionFunc (void (*func)(int x, int y)) — задает функцию, обрабатывающую движение курсора мыши, когда не зажато ни одной кнопки мыши
- void glutEntryFunc (void (*func)(int state)) — задает функцию, обрабатывающую движение курсора за пределы окна и его возвращение
- void glutTimerFunc (unsigned int msecs, void (*func)(int value), value) — задает функцию, вызываемую по таймеру
Игры на Android или iOS почти такие же, как на вашем ПК или консолях.
Поскольку игры на мобильных устройствах в настоящее время очень сложны, потребление ресурсов также увеличилось. Если у топ-телефонов нет проблем с запуском последних названий, более старые устройства в значительной степени нахмурились от резких изображений, которые приводят к неприятному опыту.
Распределение различных семейств мобильных GPU у наших игроков
Приведем статистику по мобильным GPU, собранную у наших игроков на конец 2019 года:
Ниже раскроем сегмент «Others»
Исходя из этих данных, посмотрим на распределение GPU с точки зрения их основных особенностей.
Векторные ALU (arithmetic logic unit) устаревают и заменяются скалярными. На сегодня основная масса мобильных GPU с векторным набором инструкций — это Mali Midgard, который можно считать средним по производительности. Т.к. векторизация, как правило, не замедляет выполнение на скалярных ALU, стоит рассматривать векторизацию как актуальный прием оптимизации шейдеров под mobile.
Специализированные шейдерные ядра устарели и заменяются унифицированными. Vertex Bottleneck на скелетном меше более не страшен. Специализированные ядра используются только на семействе Mali-4xx (Utgard). Напомним, что эти GPU поддерживают только OpenGL ES 2.0. У нашей аудитории их около 3.5%.
И наконец, подавляющее количество мобильных GPU используют тайловый подход. Immediate Mode стал маргинальным и быстро вытесняется вместе с видеокартами, его применяющими. Доля immediate mode GPUs у наших игроков составляет около 0.7%.
Adreno
Вторым по распространенности семейством мобильных GPU является Adreno. Эта видеокарта устанавливается на SoC, известный под брендом Snapdragon, от американской компании Qualcomm. Snapdragon устанавливается в топовых смартфонах современности от Samsung, Sony и др.
Актуальными видеокартами Adreno являются семейства cерий 3xx — 6xx. Все эти серии объединяют следующие особенности:
- унифицированные шейдерные ядра
- Pseudo TBR (большие размеры тайлов, размещающиеся в традиционной dedicated GPU memory)
- Автоматическое переключение в Immediate Mode Rendering в зависимости от характера сцены (FlexRender)
- Скалярный набор инструкций
Mali Utgard
В 2008 году на свет появились первые представители архитектуры Mali Utgard, актуальной вплоть до сегодняшнего дня. Эти видеокарты именуются по схеме Mali-4xx MPn, где xx — номер модельного ряда, а n — количество фрагментных ядер. В Mali Utgard шейдерные ядра специализированные, и во всех моделях устанавливалось только 1 вершинное ядро.
Другие особенности архитектуры Mali Utgard:
- OpenGL ES 2.0
- Отсутствие поддержки highp во фрагментных ядрах
- Векторный набор инструкций (есть смысл векторизировать вычисления)
Если имеется готовность отказаться от этой аудитории, достаточно установить требование поддержки OpenGL ES 3.0 в AndroidManifest.xml:
Кроме Mali Utgard, распространенных мобильных GPU без поддержки OpenGL ES 3.0 на данный момент нету.
Отдельного внимания заслуживает использование текстур большого разрешения на Mali Utgard. Десять бит мантиссы при точности mediump не хватает для качественного текстурирования с разрешением текстур более 1024 на одну из сторон. Однако, несмотря на поддержку только mediump точности вычислений во фрагментных ядрах Mali Utgard, можно получить fp24 точность текстурных координат при использовании varying напрямую.
В качестве бонуса на некоторых архитектурах такой подход позволяет осуществлять prefetch текстурного содержимого до выполнения fragment shader, что минимизирует stalls при ожидании результатов текстурных выборок.
Именования
Графика
- GL_POINTS — каждая вершина задает точку
- GL_LINES — каждая отдельная пара вершин задает линию
- GL_LINE_STRIP — каждая пара вершин задает линию (т.е. конец предыдущей линии является началом следующей)
- GL_LINE_LOOP — аналогично предыдущему за исключением того, что последняя вершина соединяется с первой и получается замкнутая фигура
- GL_TRIANGLES — каждая отдельная тройка вершин задает треугольник
- GL_TRIANGLE_STRIP — каждая следующая вершина задает треугольник вместе с двумя предыдущими (получается лента из треугольников)
- GL_TRIANGLE_FAN — каждый треугольник задается первой вершиной и последующими парами (т.е. треугольники строятся вокруг первой вершины, образуя нечто похожее на диафрагму)
- GL_QUADS — каждые четыре вершины образуют четырехугольник
- GL_QUAD_STRIP — каждая следующая пара вершин образует четырехугольник вместе с парой предыдущих
- GL_POLYGON — задает многоугольник с количеством углов равным количеству заданных вершин
- glBegin ( GL_QUADS ) ;
- glColor3f ( 1.0 , 1.0 , 1.0 ) ;
- glVertex2i ( 250 , 450 ) ;
- glColor3f ( 0.0 , 0.0 , 1.0 ) ;
- glVertex2i ( 250 , 150 ) ;
- glColor3f ( 0.0 , 1.0 , 0.0 ) ;
- glVertex2i ( 550 , 150 ) ;
- glColor3f ( 1.0 , 0.0 , 0.0 ) ;
- glVertex2i ( 550 , 450 ) ;
- glEnd ( ) ;
Открытость Imagination Technologies
Создатели PowerVR предоставили в открытый доступ больше документации по сравнению с другими разработчиками GPU. Детально описана архитектура графического конвейера, а также набор инструкций для архитектуры Rogue. Существует удобный инструмент PVRShaderEditor, позволяющий на лету получать профилировочную информацию по шейдеру, а также его дизассемблерный листинг для Rogue.
Несмотря на ограниченное присутствие видеокарт PowerVR в среде устройств на базе Android, есть смысл изучать их архитектуры для грамотного программирования графики под iOS.
Устройство OpenGL
OpenGL можно сравнить с большим конечным автоматом, который имеет множество состояний и функций для их изменения. Под состоянием OpenGL в основном имеют ввиду контекст OpenGL. Во время работы с OpenGL мы будем проходить через несколько меняющих состояния функций, которые будут менять контекст, и выполняющие действия в зависимости от текущего состояния OpenGL.
Например, если мы перед отрисовкой передадим OpenGL команду использовать линии вместо треугольников, то OpenGL все последующие отрисовки будет использовать линии, пока мы не изменим эту опцию, или не поменяем контекст.
Adreno Tile Based Rendering
На видеокарты Adreno установлена «традиционная» GPU память, называемая GMEM. Применяются объемы от 128kb до 1536kb. Это позволяет использовать больший размер тайлов по сравнению с архитектурами других разработчиков мобильных GPU. На Adreno размер тайлов динамический и зависит от используемого формата цвета, буфера глубины и трафарета. При работе в режиме Immediate Mode рендер происходит в системную память.Существует GL ES расширение, позволяющее указать предпочтительный режим: QCOM_binning_control. Однако, последние рекомендации от Qualcomm предлагают полностью полагаться на драйвера GPU, которые сами определяют наиболее предпочтительный режим для сформированного приложением командного буфера.
При работе в режиме TBR Adreno делает 2 вершинных прохода:
- Binning pass — распределение примитивов по бинам (bins, синоним тайлов)
- Полноценный vertex pass для отрисовки только тех примитивов, которые попадают в текущий Bin
Lowp точность
PowerVR SGX — единственные актуальные мобильные GPU с аппаратной поддержкой
lowp. Более новые модели PowerVR, а также все современные GPU других вендоров фактически используют точность mediump. Использование
lowp на PowerVR SXG позволяет добиться более высокой плотности вычислений (больше операций за такт). При этом операция swizzle (перестановка компонент вектора) для lowp, в отличие от других точностей, не является бесплатной. Эта особенность, а также узкий диапазон значений, который предоставляет lowp ([-2,2]) ограничивает сферу ее применения. При этом неудачно поставленный lowp, приводящий к артефактам на семействе SGX, не будет замечен на всех остальных видеокартах, где фактически будет использоваться точность mediump. По этой причине стоит рассмотреть отказ от использования lowp в шейдерах.
Бонус, Настройки кэша и Ускорение скорости интерфейса.
За исключением настройки, которая активирует графический движок OpenGL Skia, есть два важных параметра, которые заставят ваш Android двигаться быстрее.
1. Очистить кеш из: Настройки телефона / Хранение и память / Данные кэширования. Кэшированные данные больше не полезны, поэтому вы можете удалить их без проблем.
2. Увеличьте скорость анимации и переход окон из: Параметры разработчика / Рисование / Масштаб анимации . Масштаб перехода . Масштаб длительности . установите их все на 0,5 или отключите анимацию, но все будет двигаться слишком резко, если вы отключите его; Я думаю, что 0,5 идеально.
Учебники нравится:
Активируйте OpenGL Skia для лучшей производительности в играх для Android - Видеоурок
Переход от графического движка OpenGL к OpenGL Skia.
Графический движок OpenGL Skia можно активировать с помощью:
Настройки / Параметры разработчика / Воспроизведение с аппаратным ускорением / Установка графического процессора… / выберите OpenGL Skia здесь
Читайте также: