Настройка gpu рендеринга opengl skia что это такое

Обновлено: 01.07.2024

Меня немного смущают все классы в Skia, которые, кажется, играют роль в конвейере рендеринга на каком-то этапе.

В cairo есть контекст, который отвечает за рисование, и поверхность, которая является "подклассом" для разных типов вывода и является целью, на которой я рисую.

Для Skia рисование в простом растровом буфере очень просто и требует объекта SkCanvas для рисования и SkBitmap в качестве хранилища.

Теперь я изучаю аппаратное ускорение, и тут я запутался.

  • контексты (например, GrContext)
  • устройства (например, SkGpuDevice)
  • поверхности (например, SkBitmapSurface)
  • интерфейсы (например, GrGlInterface)
  • рендерить цели
  • бэкэнды

Я не могу найти никакой реальной документации о них. Коды примеров кажутся устаревшими, так как на ключевых этапах в них используются в основном устаревшие и/или внутренние функции.

Спасибо за любой ответ, который поможет мне понять идею.

Брайан Саломон

GrGLInterface — это всего лишь список указателей на функции, которые позволяют Skia взаимодействовать с OpenGL. Мы используем его, чтобы избежать специфичных для платформы сложностей доступа к функциям OpenGL и позволить нам использовать разные реализации OpenGL в одном и том же двоичном файле (например, ANGLE и родной OpenGL в Windows).

GrContext должен сопоставляться один к одному с контекстами OpenGL, с которыми вы хотите использовать Skia. Он использует GrGLInterface для выполнения вызовов OpenGL. Вы передаете один в функцию Create factory GrContext. Если вы просто используете собственный OpenGL вашей системы, вы можете передать NULL, и один будет создан внутри (при условии, что вы построили с правильным файлом GrGLCreateNativeInterface_ .cpp для конкретной платформы).

В GrContext есть несколько функций, представляющих интерес для клиента Skia. Мы планируем разделить его интерфейс на те части, которые имеют отношение к клиентам и внутренним функциям. Просто это не было приоритетом. Единственные действительно интересные функции связаны с обертыванием объектов OpenGL, которые вы создали с помощью типов Skia, чтобы вы могли рисовать их с помощью Skia.

Если вы хотите использовать Skia для отрисовки созданной вами текстуры OpenGL, вам следует использовать GrContext::wrapBackendTexture() и указать kRenderTarget_GrBackendTextureFlag в дескрипторе, который вы передаете. Вы должны использовать SkSurface::NewRenderTargetDirect(texture- >asRenderTarget()) для создания SkSurface (см. ниже). Должен быть ярлык для выполнения всех этих действий с помощью фабричной функции SkSurface (см. ниже), и мы скоро добавим его.

Есть также функция GrContext::wrapBackendRenderTarget. Это существует для привязки Skia к FBO в OpenGL, которые не поддерживаются текстурой, а именно FBO 0. Это используется для рисования непосредственно на экране. Однако, поскольку FBO не поддерживается текстурой, он предназначен только для рендеринга, а не для источника.

SkSurface является предпочтительным способом создания контекста рисования для Skia как для sw-rasterizer, так и для gpu-rasterizer. Я уже упоминал NewRenderTargetDirect(). Непрямые версии создают объекты OpenGL внутри Skia.

Независимо от того, какой серверной частью вы создаете поверхность, для него есть доступ к холсту через SkSurface::getCanvas(). Вы вызываете отрисовку этого холста и можете делать неизменяемые снимки его содержимого с помощью SkSurface::newImageSnapshot(). Вам не нужно беспокоиться о том, какой подкласс SkSurface или SkImage вы используете, а подклассы SkDevice предназначены только для внутреннего доступа к библиотеке.

Если вы не выполняете визуализацию непосредственно на экране и готовы компоновать содержимое, визуализируемое Skia, с помощью OpenGL, вам необходимо сделать две вещи: убедиться, что Skia выдала все команды рисования с внутренней буферизацией, а затем получить идентификатор текстуры.< /p>

// Сообщаем Skia, что вы изменили состояние контекста OpenGL, чтобы он не делал предположений о состоянии.

У Skia есть реализация Vulkan для серверной части графического процессора. Серверная часть Vulkan может быть построена вместе с серверной частью OpenGL. Клиент может выбирать между реализацией OpenGL и Vulkan во время выполнения. Серверная часть Vulkan достигла паритета функций с серверной частью OpenGL. В настоящее время мы обнаруживаем, что многие драйверы Vulkan имеют ошибки, которые вызывает Skia, и для которых у нас нет обходного пути. Мы сообщаем поставщикам об ошибках по мере их обнаружения.

Windows и Linux

Чтобы создать серверную часть Vulkan, задайте skia_use_vulkan=true в args.gn .

Андроид

Бэкэнд Vulkan может работать на любом устройстве с драйверами Vulkan, включая все устройства Android N+. Чтобы создать серверную часть Vulkan, задайте ndk_api = 24 в args.gn для Android N.

Использование серверной части Vulkan

Чтобы создать GrContext, поддерживаемый Vulkan, клиент создает устройство и очередь Vulkan, инициализирует GrVkBackendContext для описания контекста, а затем вызывает GrContext::MakeVulkan:

При использовании серверной части Vulkan GrVkImageInfo используется для создания объектов GrBackendTexture и GrBackendRenderTarget, которые, в свою очередь, используются для создания объектов SkSurface и SkImage, ссылающихся на изображения VkImage, созданные клиентом Skia.

Объект GrBackendObject, возвращаемый функциями SkImage::getTextureHandle(), SkSurface::getTextureHandle() и SkSurface::getRenderTargetHandle(), следует интерпретировать как GrVkImageInfo*. Это позволяет клиенту получить резервный VkImage для SkImage или SkSurface.

GrVkImageInfo определяет VkImage и связанное с ним состояние (плитку, макет, формат и т. д.). После получения GrVkImageInfo* через getTextureHandle() или getRenderTargetHandle() клиент должен проверить поле fImageLayout, чтобы узнать, в каком макете Skia оставил VkImage перед использованием VkImage. Если клиент изменяет макет VkImage, перед возобновлением рендеринга Skia следует вызвать GrVkImageInfo::updateImageLayout(макет VkImageLayout).

Клиент несет ответственность за любую синхронизацию или барьеры, необходимые перед тем, как Skia выполнит ввод-вывод для VkImage, импортированного в Skia через GrVkImageInfo. Skia предполагает, что может начать выдачу команд, ссылающихся на VkImage, без дополнительной синхронизации.

Отказывается от всех ресурсов графического процессора и предполагает, что базовый внутренний контекст 3D API больше нельзя использовать.

Вызовите это, если вы потеряли связанный контекст графического процессора, и поэтому внутренние ссылки/идентификаторы текстуры, буфера и т. д. теперь недействительны. Вызов этого гарантирует, что деструкторы контекста и любые созданные им объекты ресурсов не будут выполнять внутренние вызовы 3D API. Контент, обработанный, но не очищенный ранее, может быть потерян. После того, как эта функция будет вызвана, все последующие вызовы контекста завершатся ошибкой или не будут выполняться.

Обычно эта функция используется, когда базовый 3D-контекст был потерян, и дальнейшие вызовы API могут привести к сбою.

Для Vulkan, даже если устройство потеряно, VkQueue, VkDevice или VkInstance, используемые для создания контекста, должны оставаться активными даже после отказа от контекста. Эти объекты должны жить в течение времени жизни самого объекта контекста. Причина этого заключается в том, что мы можем продолжать удалять любые оставшиеся GrBackendTextures/RenderTargets, которые необходимо очищать даже в состоянии потери устройства.

◆ заброшенный()

Возвращает значение true, если контекст был заброшен или конкретный внутренний контекст перешел в невосстановимое, утерянное состояние (например,

в бэкэнде Vulkan, если мы получили VK_ERROR_DEVICE_LOST). Если внутренний контекст потерян, этот вызов также откажется от этого контекста.

◆ addOnFlushCallbackObject()

Регистрирует объект для обратных вызовов, связанных со сбросом.

ПРИМЕЧАНИЕ: диспетчер рисунков отслеживает этот объект как необработанный указатель; вызывающая сторона должна убедиться, что его время жизни привязано к времени жизни контекста.

◆ арены()

◆ asDirectContext()

◆ asRecordingContext()

◆ checkAsyncWorkCompletion()

Проверяет, завершена ли какая-либо асинхронная работа, и если да, то вызывает связанные обратные вызовы.

◆ colorTypeSupportedAsImage() [1/2]

Можно ли создать SkImage с заданным типом цвета.

◆ colorTypeSupportedAsImage() [2/2]

Можно ли создать SkImage с заданным типом цвета.

◆ colorTypeSupportedAsSurface() [1/2]

Можно ли создать SkSurface с заданным типом цвета.

Чтобы проверить, поддерживается ли MSAA, используйте функцию maxSurfaceSampleCountForColorType().

◆ colorTypeSupportedAsSurface() [2/2]

◆ createBackendTexture() [1/8]

◆ createBackendTexture() [2/8]

Удобная версия createBackendTexture(), которая принимает только растровое изображение базового уровня.

◆ createBackendTexture() [3/8]

◆ createBackendTexture() [4/8]

< td >,
GrBackendTexture GrDirectContext::createBackendTexture ( const SkPixmap srcData[],< /td>
int numLevels,
GrSurfaceOrigin
GrRenderable ,
GrProtected ,< /td>
GrGpuFinishedProc finishedProc = nullptr ,
GrGpuFinishedContext finishedContext = nullptr
)

Если возможно, создайте фоновую текстуру, инициализированную предоставленными данными растрового изображения.

Клиент должен убедиться, что возвращаемая серверная текстура действительна. Клиент может передать готовую процедуру, чтобы получить уведомление, когда данные были загружены графическим процессором и текстура может быть удалена. Клиент должен вызвать submit, чтобы отправить загрузку на GPU. FinishProc всегда будет вызываться, даже если нам не удалось создать GrBackendTexture. В случае успеха созданная внутренняя текстура будет совместима с предоставленными растровыми изображениями. Совместимость в данном случае означает, что внутренний формат будет результатом вызова defaultBackendFormat для базового типа растрового изображения.Данные src могут быть удалены, когда этот вызов вернется. Если numLevels равно 1, получится текстура без mipMapped. Если желательна текстура mipMapped, должны быть предоставлены данные для всех уровней mipmap. В случае мипмаппинга все цветотипы предоставленных растровых изображений должны быть одинаковыми. Кроме того, все мип-уровни должны иметь правильный размер (см. SkMipmap::ComputeLevelSize и ComputeLevelCount). GrSurfaceOrigin определяет, перевернуты ли растровые данные в текстуре по вертикали. Примечание: альфа-типы и цветовые пространства растрового изображения игнорируются. Для серверной части Vulkan макет созданного VkImage будет выглядеть так: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL

◆ createBackendTexture() [5/8]

GrBackendTexture GrDirectContext::createBackendTexture ( int ширина,
int height,
const GrBackendFormat & ,
const SkColor4f & color,
GrMipmapped ,
GrRenderable ,
GrRenderable = GrProtected::kNo ,
GrGpuFinishedProc finishedProc = nullptr ,
GrGpuFinishedContext finishedContext = nullptr
)

Если возможно, создайте фоновую текстуру, инициализированную для определенного цвета.

Клиент должен убедиться, что возвращаемая серверная текстура действительна. Клиент может передать готовую процедуру, чтобы получить уведомление, когда данные были загружены графическим процессором и текстура может быть удалена. Клиент должен вызвать submit, чтобы отправить загрузку на GPU. FinishProc всегда будет вызываться, даже если нам не удалось создать GrBackendTexture. Для серверной части Vulkan макет созданного VkImage будет выглядеть так: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL

◆ createBackendTexture() [6/8]

Явно выделенный API-интерфейс внутренних текстур позволяет клиентам использовать Skia для создания внутренних объектов вне собственно Skia (т. е. система кэширования Skia не будет знать о них).

Клиент несет ответственность за удаление всех этих объектов (используя deleteBackendTexture) перед удалением контекста, использованного для их создания. Если бэкэнд Vulkan, текстуры должны быть удалены, прежде чем отказаться от контекста. Кроме того, клиенты должны удалять эти объекты только в том потоке, для которого этот контекст активен.

Клиент отвечает за обеспечение синхронизации между различными вариантами использования внутреннего объекта (т. е. его перенос на поверхность, отрисовка на нем, удаление поверхности, повторный перенос его на изображение и отрисовка изображения потребует явной синхронизации на клиентском компьютере). часть). Если возможно, создайте неинициализированную внутреннюю текстуру. Клиент должен убедиться, что возвращенная внутренняя текстура действительна. Для серверной части Vulkan макет созданного VkImage будет выглядеть так: VK_IMAGE_LAYOUT_UNDEFINED.

◆ createBackendTexture() [7/8]

GrBackendTexture GrDirectContext::createBackendTexture ( int ширина,
int height,
const GrBackendFormat & ,
GrMipmapped ,
GrRenderable ,
GrProtected = GrProtected::kNo
)
GrBackendTexture GrDirectContext::createBackendTexture ( int ширина,
int height,
SkColorType ,
const SkColor4f & color,
GrMipmapped ,
GrRenderable ,
GrProtected = GrProtected::kNo ,
GrGpuFinishedProc finishedProc = nullptr ,
GrGpuFinishedContext finishedContext = nullptr
)

Если возможно, создайте фоновую текстуру, инициализированную для определенного цвета.

Клиент должен убедиться, что возвращаемая серверная текстура действительна. Клиент может передать готовую процедуру, чтобы получить уведомление, когда данные были загружены графическим процессором и текстура может быть удалена. Клиент должен вызвать submit, чтобы отправить загрузку на GPU. FinishProc всегда будет вызываться, даже если нам не удалось создать GrBackendTexture. В случае успеха созданная внутренняя текстура будет совместима с предоставленным SkColorType. Для серверной части Vulkan макет созданного VkImage будет выглядеть так: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL

◆ createBackendTexture() [8/8]

GrBackendTexture GrDirectContext::createBackendTexture ( int ширина,
int height,
SkColorType ,
GrMipmapped ,
GrRenderable ,
GrProtected = GrProtected::kNo
)

Если возможно, создайте неинициализированную внутреннюю текстуру.

Клиент должен убедиться, что возвращаемая серверная текстура действительна. В случае успеха созданная внутренняя текстура будет совместима с предоставленным SkColorType. Для серверной части Vulkan макет созданного VkImage будет выглядеть так: VK_IMAGE_LAYOUT_UNDEFINED.

◆ createCompressedBackendTexture() [1/4]

GrBackendTexture GrDirectContext::createCompressedBackendTexture ( int ширина,
int height,
const GrBackendFormat & ,
const SkColor4f & color,
GrMipmapped ,
GrProtected = GrProtected::kNo ,
GrGpuFinishedProc finishedProc = nullptr ,
GrGpuFinishedContext finishedContext = nullptr
)

Если возможно, создайте сжатую фоновую текстуру, инициализированную определенным цветом.

Клиент должен убедиться, что возвращаемая серверная текстура действительна. Клиент может передать готовую процедуру, чтобы получить уведомление, когда данные были загружены графическим процессором и текстура может быть удалена. Клиент должен вызвать submit, чтобы отправить загрузку на GPU. FinishProc всегда будет вызываться, даже если нам не удалось создать GrBackendTexture. Для серверной части Vulkan макет созданного VkImage будет выглядеть так: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL

◆ createCompressedBackendTexture() [2/4]

Если возможно, создайте внутреннюю текстуру, инициализированную предоставленными необработанными данными.

Клиент должен убедиться, что возвращаемая серверная текстура действительна. Клиент может передать готовую процедуру, чтобы получить уведомление, когда данные были загружены графическим процессором и текстура может быть удалена. Клиент должен вызвать submit, чтобы отправить загрузку на GPU. FinishProc всегда будет вызываться, даже если нам не удалось создать GrBackendTexture. Если numLevels равно 1, результатом будет текстура без mipMapped. Если желательна текстура mipMapped, должны быть предоставлены данные для всех уровней mipmap. Кроме того, все мип-уровни должны иметь правильный размер (см. SkMipmap::ComputeLevelSize и ComputeLevelCount). Для серверной части Vulkan макет созданного VkImage будет выглядеть так: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL

◆ createCompressedBackendTexture() [3/4]

GrBackendTexture GrDirectContext::createCompressedBackendTexture ( int ширина,
int height,
const GrBackendFormat & ,
const void * данные,
size_t dataSize,
GrMipmapped ,
GrProtected = GrProtected::kNo ,
GrGpuFinishedProc finishedProc = nullptr ,
GrGpuFinishedContext finishedContext = nullptr
)
< td >,
GrBackendTexture GrDirectContext::createCompressedBackendTexture ( int ширина,
int высота,
SkImage::CompressionType
const SkColor4f & цвет,
GrMipmapped ,
GrProtected = GrProtected::kNo ,
GrGpuFinishedProc finishedProc = nullptr ,
GrGpuFinishedContext finishedContext = nullptr
)

◆ createCompressedBackendTexture() [4/4]

< td >,

◆ defaultBackendFormat() [1/2]

Получить GrBackendFormat по умолчанию для данного SkColorType и возможности рендеринга.

Гарантируется, что этот внутренний формат будет использоваться следующими методами createBackendTexture на основе SkColorType и SkSurfaceCharacterization.

Вызывающий должен проверить правильность возвращаемого формата.

◆ defaultBackendFormat() [2/2]

◆ удалитьBackendTexture()

◆ уничтожитьDrawingManager()

◆ detachArenas()

◆ detachProgramData()

◆ directContextID()

◆ DrawingManager()

◆ дампJSON()

◆ dumpMemoryStatistics()

GrBackendTexture GrDirectContext::createCompressedBackendTexture ( int ширина,
int высота,
SkImage::CompressionType
const void * данные,
size_t dataSize,
GrMipmapped ,
GrProtected = GrProtected::kNo ,
GrGpuFinishedProc finishedProc = nullptr ,
GrGpuFinishedContext finishedContext = nullptr
)
void GrDirectContext::dumpMemoryStatistics ( SkTraceMemoryDump * traceMemoryDump ) const

Перечисляет все кэшированные ресурсы графического процессора и выгружает их память в traceMemoryDump.

◆ смыть() [1/2]

◆ смыть() [2/2]

Вызовите, чтобы гарантировать, что все рисунки в контексте были сброшены на базовые объекты, специфичные для 3D API.

Вызов отправки всегда необходим, чтобы убедиться, что работа действительно отправлена ​​на графический процессор. Некоторые особенности API: GL: команды фактически отправляются драйверу, но glFlush никогда не вызывается. Таким образом, некоторые объекты синхронизации из флеша не будут действительны, пока не произойдет отправка.

Vulkan/Metal/D3D/Dawn. Команды записываются в API серверной части, соответствующие буферу команд или объектам кодировщика. Однако эти объекты не отправляются на графический процессор, пока не произойдет отправка.

Если возвращено значение GrSemaphoresSubmitted::kYes, только инициализированные GrBackendSemaphores будут переданы графическому процессору во время следующего вызова отправки (возможно, Skia не удалось создать подмножество семафоров). Клиент не должен ждать этих семафоров до тех пор, пока не будет вызвана функция submit, и должен поддерживать их активность до тех пор. Если этот вызов возвращает GrSemaphoresSubmitted::kNo, серверная часть GPU не будет отправлять какие-либо семафоры для передачи сигналов на GPU. Таким образом, у клиента не должно быть ожидания GPU ни на одном из семафоров, переданных с помощью GrFlushInfo. Независимо от того, были ли семафоры переданы графическому процессору или нет, клиент по-прежнему несет ответственность за удаление любых инициализированных семафоров. Независимо от отправки семафора контекст все равно будет очищен. Следует подчеркнуть, что возвращаемое значение GrSemaphoresSubmitted::kNo не означает, что сброса не произошло. Это просто означает, что семафоры не были переданы графическому процессору. Вызывающий должен считать это ошибкой только в том случае, если он передал семафоры для отправки.

◆ flushAndSubmit()

Вызов, чтобы убедиться, что все рисунки в контексте были сброшены и отправлены в базовый 3D API.

Это эквивалентно вызову GrContext::flush со значением по умолчанию GrFlushInfo, за которым следует GrContext::submit(syncCpu).

Когда рендеринг с помощью графического процессора появился на Android, он был, мягко говоря, ненадежным. Официальная цель — повысить производительность приложений, но в то время многие графические пользовательские интерфейсы не знали, как работать с рендерингом на GPU. В некоторых случаях это было даже медленнее, чем программный рендеринг.

Со временем, особенно после выхода Android 4.0, GPU стал более надежным и постепенно стал использоваться по умолчанию для большинства приложений. Теперь в большинство обновленных приложений встроен рендеринг с помощью графического процессора.

Но прежде чем мы перейдем к тому, как и когда включать эту опцию, давайте разберемся, как она работает.

Что такое GPU-рендеринг?

Графический процессор — это графический процессор. По своей сути он очень похож на ЦП, но вместо выполнения вычислений и выполнения задач, связанных с операционной системой и оборудованием, ГП обрабатывает графическую информацию. Другими словами, он выводит изображение на экран для ваших глаз.

Хотя ЦП прекрасно справляется с графическими инструкциями, это отнимет время у выполнения других важных для системы задач, что может привести к задержкам. Более того, конструкция ЦП делает их весьма неэффективными при обработке графических данных по сравнению с ГП, запрограммированными на обработку графической информации.

Именно здесь на помощь приходит рендеринг с использованием графического процессора: он отнимает часть обработки графики от центрального процессора, тем самым освобождая его для более важных задач. Поскольку графический процессор гораздо лучше справляется с графическими данными, конечным результатом является более высокая производительность как для центрального, так и для графического процессора.

Когда использовать GPU-рендеринг

Включение этого параметра перенесет компоненты окна, такие как текст, кнопки и расчеты 2D-графики, на графический процессор. Это улучшит отображение анимации пользовательского интерфейса на вашем устройстве и уменьшит задержки. Хотя вы определенно добьетесь более плавной работы и более высокой частоты кадров в 2D-приложениях, ваше устройство может в конечном итоге потреблять больше заряда батареи. Известно, что графические процессоры потребляют больше энергии, чем ЦП, поэтому ожидайте, что время работы от батареи сократится на 10–15 %, если вы оставите его постоянно включенным.

Принудительный рендеринг с помощью графического процессора определенно имеет смысл на устройствах с более слабым процессором.Если ваше устройство не четырехъядерное, я бы рекомендовал вам всегда оставлять его включенным.

Но имейте в виду, что рендеринг с помощью графического процессора эффективен только для 2D-приложений. Большие игры, использующие 3D-графику, могут иметь худшую частоту кадров при включенном Force GPU Rendering. Хорошо, что большинство версий Android не будут мешать 3D-приложениям и будут принудительно использовать GPU-рендеринг только в 2D-приложениях, которые не используют его по умолчанию.

Поскольку в большинстве новых приложений этот параметр уже включен в коде, вы можете заметить существенные различия только при просмотре меню вашего телефона. Ваше устройство будет работать быстрее и отображать информацию на экране быстрее, чем раньше. Конечно, некоторые старые или плохо сделанные приложения достигают более высокой частоты кадров при принудительном рендеринге с помощью графического процессора, но такие случаи редки.

Суть в том, что вам решать, хотите ли вы обменять время автономной работы на повышенную плавность и дополнительную частоту кадров. Имея это в виду, вот как включить Force GPU Rendering.

Читайте также: