Interfejsy RenderScript API są wycofywane od Androida 12. Producenci urządzeń i komponentów już przestali obsługiwać akcelerację sprzętową, a obsługę RenderScriptu prawdopodobnie całkowicie wycofamy w przyszłej wersji.
Wydajność C/C++ może być odpowiednia w wielu przypadkach, a jeśli opierasz się tylko na RenderScript w przypadku wewnętrznych zasobów, możesz je zastąpić zestawem zamiennym RenderScript Intrinsics, który jest łatwiejszy w użyciu i potencjalnie dwukrotnie zwiększa wydajność.
Jeśli chcesz w pełni wykorzystać możliwości akceleracji GPU, zalecamy przeniesienie skryptów do interfejsu Vulkan lub korzystanie z innych opcji przyspieszających, takich jak migracja skryptów do trybu OpenGL, operacje graficzne oparte na kanwa lub Android Graphics Shading Language (AGSL).
W związku z wycofaniem z platformy Androida obsługi RenderScriptu przestaniemy ją obsługiwać we wtyczce Androida do obsługi Gradle. Od wersji 7.2 wtyczki Androida do obsługi Gradle interfejsy API RenderScript zostały wycofane. Nadal działają, ale wywołują ostrzeżenia. Przyszłe wersje AGP nie będą już obsługiwać Renderscript. Ten przewodnik wyjaśnia, jak przeprowadzić migrację z renderScriptu.
Migracja z usługi wewnętrznego
Chociaż wewnętrzne funkcje RenderScriptu nadal działają po wycofaniu tej usługi, mogą być wykonywane tylko na procesorze, a nie na procesorach graficznych.
W przypadku niektórych z tych operacji dostępne są teraz wydajniejsze opcje wbudowane w platformę lub w biblioteki Jetpack.
Wbudowane przyspieszone operacje na zdjęciach
Platforma Android obsługuje przyspieszone operacje przetwarzania obrazów, które można stosować do obrazów niezależnie od elementu RenderScript. Przykłady:
- Różne
- Rozmycie
- Matryca kolorów
- Zmień rozmiar
Rozmycie obrazu w Androidzie 12 i nowszych
Do Androida 12 (poziomu interfejsu API 31) dodano funkcję RenderEffect
z obsługą rozmycia, która umożliwia rozmycie elementu RenderNode
. RenderNode
to konstrukcja listy wyświetlania, której Android używa do przyspieszania grafiki na platformie.
Android udostępnia skrót do zastosowania efektu do elementu RenderNode
powiązanego z elementem View
. Aby rozmyć obiekt View
, wywołaj View.setRenderEffect()
:
val blurRenderEffect = RenderEffect.createBlurEffect(radius, radius,
Shader.TileMode.MIRROR
)
view.setRenderEffect(blurRenderEffect)
Rozmycie obrazu na Androidzie 12 lub nowszym wyrenderowane w postaci mapy bitowej
Jeśli potrzebujesz rozmytego obrazu wyrenderowanego w interfejsie Bitmap
, platforma obsługuje przyspieszone renderowanie z użyciem platformy HardwareRenderer
wspieranej przez HardwareBuffer
. Ten kod tworzy HardwareRenderer
, RenderNode
i RenderEffect
do rozmycia:
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)
Zastosowanie efektu wymaga użycia wewnętrznego RecordingCanvas
dla RenderNode
. Ten kod rejestruje rysunek, tworzy żądanie renderowania, a następnie czeka na zakończenie żądania:
val renderCanvas = it.renderNode.beginRecording()
renderCanvas.drawBitmap(it.bitmap, 0f, 0f, null)
renderNode.endRecording()
hardwareRenderer.createRenderRequest()
.setWaitForPresent(true)
.syncAndDraw()
Wyrenderowany obraz znajduje się w elemencie HardwareBuffer
powiązanym z elementem ImageReader
. Poniższy kod pobiera Image
i zwraca element Bitmap
, który opakowuje 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")
Ten kod jest czyszczony po wyrenderowaniu obrazu. Pamiętaj, że ImageReader
, RenderNode
, RenderEffect
i HardwareRenderer
mogą być używane do przetwarzania wielu obrazów.
hardwareBuffer.close()
image.close()
imageReader.close()
renderNode.discardDisplayList()
hardwareRenderer.destroy()
AGSL na potrzeby przetwarzania obrazu
Język AGSL (Android Graphics Shading Language) jest używany w Androidzie 13 i nowszych do definiowania zachowania programowalnych obiektów RuntimeShader
. AGSL korzysta w znacznej części z cieniowania fragmentów GLSL, ale działa w systemie renderowania grafiki na Androidzie, aby dostosowywać malowanie w obrębie Canvas
i filtrować treści w View
. Można jej używać, aby dodawać niestandardowe przetwarzanie obrazu podczas operacji rysowania lub za pomocą polecenia RenderNode
bezpośrednio renderować obraz w obszarze roboczym Bitmap
. Poniższy przykład pokazuje, jak zastosować niestandardowy program do cieniowania, aby zastąpić efekt rozmycia obrazu.
Zacznij od utworzenia instancji RuntimeShader
z użyciem kodu do cieniowania AGSL. Ten program do cieniowania służy do stosowania matrycy kolorów do rotacji kolorów:
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;
}
""")
Program do cieniowania można zastosować do elementu RenderNode
, tak jak każdy inny element typu RenderEffect
.
Poniższy przykład pokazuje, jak ustawić mundury w 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)
Aby uzyskać Bitmap
, użyj tej samej metody co w poprzedniej próbce rozmycia obrazu.
- Wewnętrzna instancja
RecordingCanvas
wRenderNode
stosuje cieniowanie. - Parametr
Image
jest pozyskiwany i zwracaBitmap
, który opakowuje swójHardwareBuffer
.
Konwertuj z planarnej YUV na RGB za pomocą CameraX
Konwersja z planarnej YUV na RGB na potrzeby przetwarzania obrazu jest dostępna w ramach Analizy obrazu w aplikacji CameraX firmy Jetpack.
Zasoby dotyczące korzystania z ImageAnalysis
są dostępne w ramach ćwiczenia z programowania Getting started with CameraX (w języku angielskim) oraz w repozytorium przykładów kamer w Androidzie.
Pakiet narzędzi do zastępowania wewnętrznych skryptów Renderscript
Jeśli Twoja aplikacja używa elementów wewnętrznych, możesz użyć samodzielnej biblioteki zastępczej. Z naszych testów wynika, że jest to szybsze niż użycie istniejącej implementacji procesora RenderScript.
Zestaw obejmuje następujące funkcje:
- Różne
- Rozmycie
- Matryca kolorów
- Obrotowy
- Histogram i histogramDot
- Tabela przeglądowa (LUT) i LUT 3D
- Zmień rozmiar
- YUV do RGB
Szczegółowe informacje i ograniczenia znajdziesz w dokumentach README.md
i Toolkit.kt
.
.
Aby pobrać bibliotekę, dodać ją i jej używać, wykonaj te czynności:
Pobierz projekt z GitHuba.
Zlokalizuj i utwórz
renderscript-toolkit module
.Dodaj bibliotekę do projektu Android Studio, modyfikując plik
build.gradle
aplikacji.Wywołaj odpowiednią metodę narzędzia.
Przykład: migracja z funkcji ScriptIntrinsicBlur
Aby zastąpić funkcję ScriptIntrinsicBlur
:
Aby rozmyć bitmapę, wywołaj polecenie
Toolkit.blur
.var blurredBitmap = Toolkit.blur(myBitmap, radius)
Jeśli chcesz rozmyć obraz reprezentowany przez tablicę bajtów, podaj szerokość, wysokość i liczbę bajtów na piksel.
val outArray = Toolkit.blur(inputArray, bytesPerPixel, width, height, radius)
Migracja ze skryptów
Jeśli Twojego przypadku użycia nie da się rozwiązać w ten sposób:
- Pakiet narzędzi do zastąpienia RenderScript Intrinsics Replacement Toolkit
- nowe interfejsy API na platformie Androida, np.
RenderEffect
iAGSL
; - Interfejsy API biblioteki Androida Jetpack, takie jak
CameraX
W Twoim przypadku użycia może też skorzystać z akceleracji GPU, ponieważ Android obsługuje obliczenia GPU w interfejsach API międzyplatformowych Vulkan i OpenGL ES (GLES). Może to być niepotrzebne, ponieważ na większości urządzeń Twoje skrypty są już uruchomione na procesorze, a nie na GPU. W niektórych przypadkach język C/C++ może być szybszy niż RenderScript, GLES czy Vulkan. (przynajmniej tak szybko, jak to możliwe w Twoim przypadku użycia).
Aby lepiej zrozumieć, jak przeprowadzić migrację, przejrzyj przykładową aplikację. Ten przykład pokazuje, jak zarówno rozmyć bitmapę, jak i przeprowadzić konwersję matrycy kolorów w RenderScript. Ten kod ma odpowiednik w językach Vulkan i OpenGL.
Jeśli Twoja aplikacja musi obsługiwać różne wersje, użyj RenderScript na urządzeniach z Androidem 6 (poziom interfejsu API 23) lub starszym oraz na obsługiwanych urządzeniach z Androidem 7 (poziom interfejsu API 24) lub nowszym oraz interfejsu Vulkan lub GLES. Jeśli Twój minSdkVersion
ma wersję 24 lub nowszą, być może nie musisz używać RenderScriptu. Możesz używać interfejsu Vulkan lub GLES 3.1 wszędzie tam, gdzie potrzebujesz obsługi mocy obliczeniowej GPU.
Android zapewnia powiązania SDK dla interfejsów API GLES, dlatego nie trzeba używać NDK podczas pracy w trybie OpenGL ES.
Vulkan nie udostępnia powiązań pakietów SDK, więc nie ma bezpośredniego mapowania z RenderScript do Vulkan. Piszesz kod Vulkan za pomocą pakietu NDK i tworzysz funkcje JNI, aby uzyskać dostęp do kodu w kotlinach lub Javie.
Na kolejnych stronach omawiamy aspekty migracji z renderScriptu. Przykładowa aplikacja uwzględnia prawie wszystkie te kwestie. Aby je lepiej zrozumieć, porównaj kod RenderScript z odpowiednikiem w interfejsie Vulkan.