Миграция с RenderScript,Миграция с RenderScript

API-интерфейсы RenderScript устарели, начиная с Android 12. Производители устройств и компонентов уже прекратили предоставлять поддержку аппаратного ускорения, и ожидается, что поддержка RenderScript будет полностью удалена в будущем выпуске.

Производительности C/C++ может быть достаточно для многих случаев использования, и если вы полагались только на RenderScript для встроенных функций, вы можете заменить это использование набором инструментов замены RenderScript Intrinsics , который проще в использовании и потенциально повышает производительность в 2 раза!

Если вам необходимо в полной мере воспользоваться преимуществами ускорения графического процессора, мы рекомендуем перенести ваши сценарии в Vulkan . Другие варианты ускорения включают миграцию ваших сценариев в OpenGL , использование операций с изображениями на основе Canvas или использование языка шейдинга графики Android (AGSL) .

После прекращения поддержки RenderScript на платформе Android поддержка RenderScript удаляется из плагина Android Gradle. Начиная с плагина Android Gradle 7.2, API RenderScript устарели. Они продолжают работать, но вызывают предупреждения. Будущие версии AGP больше не будут включать поддержку Renderscript. В этом руководстве объясняется, как перейти с RenderScript.

Миграция с встроенных функций

Хотя внутренние функции RenderScript продолжают работать после прекращения поддержки RenderScript, они могут выполняться только на ЦП, а не на графическом процессоре.

Для некоторых из этих операций теперь есть более эффективные варианты, встроенные в платформу или в библиотеки Jetpack.

Встроенные ускоренные операции с изображениями

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

  • Смешивать
  • Размытие
  • Цветовая матрица
  • Изменить размер

Размытие изображения на Android 12+ в представлении

RenderEffect с поддержкой размытия был добавлен в Android 12, уровень API 31, что позволяет размыть RenderNode . RenderNode — это конструкция списка отображения, которую Android использует для ускорения графики платформы.

Android предоставляет ярлык для применения эффекта к RenderNode связанному с View . Чтобы размыть View , вызовите View.setRenderEffect() :

val blurRenderEffect = RenderEffect.createBlurEffect(radius, radius,
        Shader.TileMode.MIRROR
    )
view.setRenderEffect(blurRenderEffect)

Размытие изображения на Android 12+, преобразованное в растровое изображение

Если вам нужно, чтобы размытое изображение было преобразовано в Bitmap , платформа поддерживает ускоренный рендеринг с помощью HardwareRenderer , поддерживаемого HardwareBuffer . Следующий код создает HardwareRenderer , RenderNode и RenderEffect для размытия:

val imageReader = ImageReader.newInstance(
    bitmap.width, bitmap.height,
    PixelFormat.RGBA_8888, numberOfOutputImages,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
)
val renderNode = RenderNode("BlurEffect")
val hardwareRenderer = HardwareRenderer()

hardwareRenderer.setSurface(imageReader.surface)
hardwareRenderer.setContentRoot(renderNode)
renderNode.setPosition(0, 0, imageReader.width, imageReader.height)
val blurRenderEffect = RenderEffect.createBlurEffect(
    radius, radius,
    Shader.TileMode.MIRROR
)
renderNode.setRenderEffect(blurRenderEffect)

Применение эффекта предполагает использование внутреннего RecordingCanvas для RenderNode . Следующий код записывает рисунок, создает запрос на отрисовку, а затем ожидает завершения запроса:

val renderCanvas = it.renderNode.beginRecording()
renderCanvas.drawBitmap(it.bitmap, 0f, 0f, null)
renderNode.endRecording()
hardwareRenderer.createRenderRequest()
    .setWaitForPresent(true)
    .syncAndDraw()

Отрисованное изображение находится в HardwareBuffer , связанном с ImageReader . Следующий код получает Image и возвращает Bitmap , которое оборачивает его HardwareBuffer .

val image = imageReader.acquireNextImage() ?: throw RuntimeException("No Image")
val hardwareBuffer = image.hardwareBuffer ?: throw RuntimeException("No HardwareBuffer")
val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null)
    ?: throw RuntimeException("Create Bitmap Failed")

Следующий код выполняет очистку после рендеринга изображения. Обратите внимание, что ImageReader , RenderNode , RenderEffect и HardwareRenderer можно использовать для обработки нескольких изображений.

hardwareBuffer.close()
image.close()
imageReader.close()
renderNode.discardDisplayList()
hardwareRenderer.destroy()

AGSL для обработки изображений

Язык шейдинга графики Android (AGSL) используется в Android 13+ для определения поведения программируемых объектов RuntimeShader . AGSL во многом разделяет свой синтаксис с фрагментными шейдерами GLSL, но работает в системе рендеринга графики Android как для настройки рисования в Canvas , так и для фильтрации содержимого View . Это можно использовать для добавления пользовательской обработки изображения во время операций рисования или путем непосредственного использования RenderNode для рендеринга изображения в Bitmap полотно. В следующем примере показано, как применить собственный шейдер для замены эффекта размытия изображения.

Начните с создания RuntimeShader , создав его экземпляр с помощью кода шейдера AGSL. Этот шейдер используется для применения цветовой матрицы для вращения оттенков:

val hueShader = RuntimeShader("""
    uniform float2 iResolution;       // Viewport resolution (pixels)
    uniform float2 iImageResolution;  // iImage1 resolution (pixels)
    uniform float iRadian;            // radian to rotate things around
    uniform shader iImage1;           // An input image
    half4 main(float2 fragCoord) {
    float cosR = cos(iRadian);
    float sinR = sin(iRadian);
        mat4 hueRotation =
        mat4 (
                0.299 + 0.701 * cosR + 0.168 * sinR, //0
                0.587 - 0.587 * cosR + 0.330 * sinR, //1
                0.114 - 0.114 * cosR - 0.497 * sinR, //2
                0.0,                                 //3
                0.299 - 0.299 * cosR - 0.328 * sinR, //4
                0.587 + 0.413 * cosR + 0.035 * sinR, //5
                0.114 - 0.114 * cosR + 0.292 * sinR, //6
                0.0,                                 //7
                0.299 - 0.300 * cosR + 1.25 * sinR,  //8
                0.587 - 0.588 * cosR - 1.05 * sinR,  //9
                0.114 + 0.886 * cosR - 0.203 * sinR, //10
                0.0,                                 //11
                0.0, 0.0, 0.0, 1.0 );                //12,13,14,15
        float2 scale = iImageResolution.xy / iResolution.xy;
        return iImage1.eval(fragCoord * scale)*hueRotation;
    }
""")

Шейдер можно применить к RenderNode , как и к любому другому RenderEffect . В следующем примере показано, как установить униформу в hueShader:

hueShader.setFloatUniform("iImageResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iRadian", radian)
hueShader.setInputShader( "iImage1", BitmapShader(bitmap, Shader.TileMode.MIRROR,
    Shader.TileMode.MIRROR))
val colorFilterEffect = RenderEffect.createShaderEffect(it.hueShader)
renderNode.setRenderEffect(colorFilterEffect)

Чтобы получить Bitmap , используется тот же метод, что и в предыдущем примере размытия изображения.

  • Внутренний RecordingCanvas для RenderNode применяет шейдер.
  • Image получается, возвращая Bitmap , которое обертывает его HardwareBuffer .

Преобразование из плоского YUV в RGB с помощью CameraX

Преобразование из плоского YUV в RGB для использования при обработке изображений поддерживается как часть варианта использования ImageAnaанализа в Jetpack's CameraX.

Ресурсы по использованию ImageAnalysis имеются в рамках лаборатории кода «Начало работы с CameraX» и в репозитории образцов камер Android.

Набор инструментов для замены встроенных функций Renderscript

Если ваше приложение использует встроенные функции, вы можете использовать автономную библиотеку замены; наши тесты показывают, что это быстрее, чем использование существующей реализации процессора RenderScript.

В набор инструментов входят следующие функции:

  • Смешивать
  • Размытие
  • Цветовая матрица
  • Свернуть
  • Гистограмма и гистограммаDot
  • Таблица поиска (LUT) и LUT 3D
  • Изменить размер
  • YUV в RGB

Полную информацию и ограничения см. в файлах README.md и Toolkit.kt набора инструментов. файлы.

Выполните следующие шаги, чтобы загрузить, добавить и использовать библиотеку:

  1. Загрузите проект с GitHub.

  2. Найдите и соберите renderscript-toolkit module .

  3. Добавьте библиотеку в свой проект Android Studio, изменив файл build.gradle приложения.

  4. Вызовите соответствующий метод набора инструментов.

Пример: миграция из функции ScriptIntrinsicBlur

Чтобы заменить функцию ScriptIntrinsicBlur :

  • Чтобы размыть растровое изображение, вызовите Toolkit.blur .

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • Если вы хотите размыть изображение, представленное массивом байтов, укажите ширину, высоту и количество байтов на пиксель.

    val outArray = Toolkit.blur(inputArray, bytesPerPixel, width, height, radius)
    

Миграция со скриптов

Если ваш вариант использования не может быть решен с помощью:

И ваш вариант использования может выиграть от ускорения графического процессора: Android поддерживает вычисления графического процессора на кроссплатформенных API-интерфейсах Vulkan и OpenGL ES (GLES). Вам это может показаться ненужным, поскольку на большинстве устройств ваши сценарии уже выполняются на ЦП, а не на графическом процессоре: в некоторых случаях C/C++ может выполнять вычисления быстрее, чем RenderScript, GLES или Vulkan. (или, по крайней мере, достаточно быстро для вашего варианта использования)

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

Если вашему приложению необходимо поддерживать ряд выпусков, используйте RenderScript для устройств под управлением Android 6 (уровень API 23) и ниже, а также Vulkan или GLES на поддерживаемых устройствах с Android 7 (уровень API 24) и выше. Если ваша minSdkVersion равна 24 или выше, возможно, вам не понадобится использовать RenderScript; Vulkan или GLES 3.1 можно использовать везде, где вам нужна поддержка вычислений на графическом процессоре.

Android предоставляет привязки SDK для API GLES, поэтому нет необходимости использовать NDK при работе в OpenGL ES.

Vulkan не предоставляет привязки SDK, поэтому нет прямого сопоставления RenderScript с Vulkan; вы пишете код Vulkan, используя NDK, и создаете функции JNI для доступа к этому коду из Kotlin или Java.

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

,

API-интерфейсы RenderScript устарели, начиная с Android 12. Производители устройств и компонентов уже прекратили предоставлять поддержку аппаратного ускорения, и ожидается, что поддержка RenderScript будет полностью удалена в будущем выпуске.

Производительности C/C++ может быть достаточно для многих случаев использования, и если вы полагались только на RenderScript для встроенных функций, вы можете заменить это использование набором инструментов замены RenderScript Intrinsics , который проще в использовании и потенциально увеличивает производительность в 2 раза!

Если вам необходимо в полной мере воспользоваться преимуществами ускорения графического процессора, мы рекомендуем перенести ваши сценарии на Vulkan . Другие варианты ускорения включают миграцию ваших сценариев на OpenGL , использование операций с изображениями на основе Canvas или использование языка шейдинга графики Android (AGSL) .

После прекращения поддержки RenderScript на платформе Android поддержка RenderScript удаляется из плагина Android Gradle. Начиная с плагина Android Gradle 7.2, API RenderScript устарели. Они продолжают работать, но выдают предупреждения. Будущие версии AGP больше не будут включать поддержку Renderscript. В этом руководстве объясняется, как перейти с RenderScript.

Миграция с встроенных функций

Хотя внутренние функции RenderScript продолжают работать после прекращения поддержки RenderScript, они могут выполняться только на ЦП, а не на графическом процессоре.

Для некоторых из этих операций теперь есть более эффективные варианты, встроенные в платформу или в библиотеки Jetpack.

Встроенные ускоренные операции с изображениями

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

  • Смешивать
  • Размытие
  • Цветовая матрица
  • Изменить размер

Размытие изображения на Android 12+ в представлении

RenderEffect с поддержкой размытия был добавлен в Android 12, уровень API 31, что позволяет размыть RenderNode . RenderNode — это конструкция списка отображения, которую Android использует для ускорения графики платформы.

Android предоставляет ярлык для применения эффекта к RenderNode связанному с View . Чтобы размыть View , вызовите View.setRenderEffect() :

val blurRenderEffect = RenderEffect.createBlurEffect(radius, radius,
        Shader.TileMode.MIRROR
    )
view.setRenderEffect(blurRenderEffect)

Размытие изображения на Android 12+, преобразованное в растровое изображение

Если вам нужно, чтобы размытое изображение было преобразовано в Bitmap , платформа поддерживает ускоренный рендеринг с помощью HardwareRenderer , поддерживаемого HardwareBuffer . Следующий код создает HardwareRenderer , RenderNode и RenderEffect для размытия:

val imageReader = ImageReader.newInstance(
    bitmap.width, bitmap.height,
    PixelFormat.RGBA_8888, numberOfOutputImages,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
)
val renderNode = RenderNode("BlurEffect")
val hardwareRenderer = HardwareRenderer()

hardwareRenderer.setSurface(imageReader.surface)
hardwareRenderer.setContentRoot(renderNode)
renderNode.setPosition(0, 0, imageReader.width, imageReader.height)
val blurRenderEffect = RenderEffect.createBlurEffect(
    radius, radius,
    Shader.TileMode.MIRROR
)
renderNode.setRenderEffect(blurRenderEffect)

Применение эффекта предполагает использование внутреннего RecordingCanvas для RenderNode . Следующий код записывает рисунок, создает запрос на отрисовку, а затем ожидает завершения запроса:

val renderCanvas = it.renderNode.beginRecording()
renderCanvas.drawBitmap(it.bitmap, 0f, 0f, null)
renderNode.endRecording()
hardwareRenderer.createRenderRequest()
    .setWaitForPresent(true)
    .syncAndDraw()

Отрисованное изображение находится в HardwareBuffer , связанном с ImageReader . Следующий код получает Image и возвращает Bitmap , которое оборачивает его HardwareBuffer .

val image = imageReader.acquireNextImage() ?: throw RuntimeException("No Image")
val hardwareBuffer = image.hardwareBuffer ?: throw RuntimeException("No HardwareBuffer")
val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null)
    ?: throw RuntimeException("Create Bitmap Failed")

Следующий код выполняет очистку после рендеринга изображения. Обратите внимание, что ImageReader , RenderNode , RenderEffect и HardwareRenderer можно использовать для обработки нескольких изображений.

hardwareBuffer.close()
image.close()
imageReader.close()
renderNode.discardDisplayList()
hardwareRenderer.destroy()

AGSL для обработки изображений

Язык шейдинга графики Android (AGSL) используется в Android 13+ для определения поведения программируемых объектов RuntimeShader . AGSL во многом разделяет свой синтаксис с фрагментными шейдерами GLSL, но работает в системе рендеринга графики Android как для настройки рисования в Canvas , так и для фильтрации содержимого View . Это можно использовать для добавления пользовательской обработки изображения во время операций рисования или путем непосредственного использования RenderNode для рендеринга изображения в Bitmap полотно. В следующем примере показано, как применить собственный шейдер для замены эффекта размытия изображения.

Начните с создания RuntimeShader , создав его экземпляр с помощью кода шейдера AGSL. Этот шейдер используется для применения цветовой матрицы для вращения оттенков:

val hueShader = RuntimeShader("""
    uniform float2 iResolution;       // Viewport resolution (pixels)
    uniform float2 iImageResolution;  // iImage1 resolution (pixels)
    uniform float iRadian;            // radian to rotate things around
    uniform shader iImage1;           // An input image
    half4 main(float2 fragCoord) {
    float cosR = cos(iRadian);
    float sinR = sin(iRadian);
        mat4 hueRotation =
        mat4 (
                0.299 + 0.701 * cosR + 0.168 * sinR, //0
                0.587 - 0.587 * cosR + 0.330 * sinR, //1
                0.114 - 0.114 * cosR - 0.497 * sinR, //2
                0.0,                                 //3
                0.299 - 0.299 * cosR - 0.328 * sinR, //4
                0.587 + 0.413 * cosR + 0.035 * sinR, //5
                0.114 - 0.114 * cosR + 0.292 * sinR, //6
                0.0,                                 //7
                0.299 - 0.300 * cosR + 1.25 * sinR,  //8
                0.587 - 0.588 * cosR - 1.05 * sinR,  //9
                0.114 + 0.886 * cosR - 0.203 * sinR, //10
                0.0,                                 //11
                0.0, 0.0, 0.0, 1.0 );                //12,13,14,15
        float2 scale = iImageResolution.xy / iResolution.xy;
        return iImage1.eval(fragCoord * scale)*hueRotation;
    }
""")

Шейдер можно применить к RenderNode , как и к любому другому RenderEffect . В следующем примере показано, как установить униформу в hueShader:

hueShader.setFloatUniform("iImageResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iRadian", radian)
hueShader.setInputShader( "iImage1", BitmapShader(bitmap, Shader.TileMode.MIRROR,
    Shader.TileMode.MIRROR))
val colorFilterEffect = RenderEffect.createShaderEffect(it.hueShader)
renderNode.setRenderEffect(colorFilterEffect)

Чтобы получить Bitmap , используется тот же метод, что и в предыдущем примере размытия изображения.

  • Внутренний RecordingCanvas для RenderNode применяет шейдер.
  • Image получается, возвращая Bitmap , которое обертывает его HardwareBuffer .

Преобразование из плоского YUV в RGB с помощью CameraX

Преобразование из плоского YUV в RGB для использования при обработке изображений поддерживается как часть варианта использования ImageAnaанализа в Jetpack's CameraX.

Ресурсы по использованию ImageAnalysis имеются в рамках лаборатории кода «Начало работы с CameraX» и в репозитории образцов камер Android.

Набор инструментов для замены встроенных функций Renderscript

Если ваше приложение использует встроенные функции, вы можете использовать автономную библиотеку замены; наши тесты показывают, что это быстрее, чем использование существующей реализации процессора RenderScript.

В набор инструментов входят следующие функции:

  • Смешивать
  • Размытие
  • Цветовая матрица
  • Свернуть
  • Гистограмма и гистограммаDot
  • Таблица поиска (LUT) и LUT 3D
  • Изменить размер
  • YUV в RGB

Полную информацию и ограничения см. в файлах README.md и Toolkit.kt набора инструментов. файлы.

Выполните следующие шаги, чтобы загрузить, добавить и использовать библиотеку:

  1. Загрузите проект с GitHub.

  2. Найдите и соберите renderscript-toolkit module .

  3. Добавьте библиотеку в свой проект Android Studio, изменив файл build.gradle приложения.

  4. Вызовите соответствующий метод набора инструментов.

Пример: миграция из функции ScriptIntrinsicBlur

Чтобы заменить функцию ScriptIntrinsicBlur :

  • Чтобы размыть растровое изображение, вызовите Toolkit.blur .

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • Если вы хотите размыть изображение, представленное массивом байтов, укажите ширину, высоту и количество байтов на пиксель.

    val outArray = Toolkit.blur(inputArray, bytesPerPixel, width, height, radius)
    

Миграция со скриптов

Если ваш вариант использования не может быть решен с помощью:

И ваш вариант использования может выиграть от ускорения графического процессора: Android поддерживает вычисления графического процессора на кроссплатформенных API-интерфейсах Vulkan и OpenGL ES (GLES). Вам это может показаться ненужным, поскольку на большинстве устройств ваши сценарии уже выполняются на ЦП, а не на графическом процессоре: в некоторых случаях C/C++ может выполнять вычисления быстрее, чем RenderScript, GLES или Vulkan. (или, по крайней мере, достаточно быстро для вашего варианта использования)

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

Если вашему приложению необходимо поддерживать ряд выпусков, используйте RenderScript для устройств под управлением Android 6 (уровень API 23) и ниже, а также Vulkan или GLES на поддерживаемых устройствах с Android 7 (уровень API 24) и выше. Если ваша minSdkVersion равна 24 или выше, возможно, вам не понадобится использовать RenderScript; Vulkan или GLES 3.1 можно использовать везде, где вам нужна поддержка вычислений на графическом процессоре.

Android предоставляет привязки SDK для API GLES, поэтому нет необходимости использовать NDK при работе в OpenGL ES.

Vulkan не предоставляет привязки SDK, поэтому нет прямого сопоставления RenderScript с Vulkan; вы пишете код Vulkan, используя NDK, и создаете функции JNI для доступа к этому коду из Kotlin или Java.

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