Migracja skryptów do OpenGL ES 3.1

W przypadku zadań, w których idealna jest moc obliczeniowa GPU, należy przenieść skrypty RenderScript do OpenGL ES (GLES) zezwala na aplikacje napisane w języku Kotlin, Java lub za pomocą NDK korzystanie ze sprzętu GPU. Poniżej omawiamy ogólny przegląd, który pomoże Ci korzystać z cieniowania obliczeniowego OpenGL ES 3.1 zastąpią skrypty RenderScript.

Inicjalizacja GLES

Zamiast tworzyć obiekt kontekstu RenderScript, wykonaj te czynności: aby utworzyć kontekst poza ekranem GLES za pomocą EGL:

  1. Użyj domyślnego wyświetlacza

  2. Zainicjuj EGL, używając domyślnego wyświetlania, określającego wersję GLES.

  3. Wybierz konfigurację EGL z typem powierzchni EGL_PBUFFER_BIT

  4. Użyj parametru display i konfiguracji, aby utworzyć kontekst EGL.

  5. Utwórz powierzchnię poza ekranem za pomocą eglCreatePBufferSurface Jeśli kontekst będzie używana tylko do obliczeń, może być maleńki (1 x 1) na różnych powierzchniach.

  6. Tworzenie wątku i wywołania renderowania eglMakeCurrent w wątku renderowania z użyciem interfejsu i kontekstu EGL, aby powiązać kontekst GL z wątkiem.

Przykładowa aplikacja pokazuje, jak zainicjować kontekst GLES w GLSLImageProcessor.kt Więcej informacji: EGLSurfaces i OpenGL ES.

Dane wyjściowe debugowania GLES

Funkcja uzyskiwania przydatnych błędów z trybu OpenGL korzysta z rozszerzenia do włączenia rejestrowania debugowania który ustawia wywołanie zwrotne debugowania. Aby to zrobić za pomocą pakietu SDK, glDebugMessageCallbackKHR, nigdy nie został zaimplementowany i zwraca wyjątek. Przykładowa aplikacja zawiera kod wywołania zwrotnego z kodu NDK.

Przydziały GLES

Alokację RenderScript można przenieść do zmienna tekstura pamięci masowej lub Obiekt buforowania pamięci Shader. W przypadku obrazów tylko do odczytu: może użyć Sampler Object, który umożliwia filtrowaniu.

Zasoby GLES są przydzielane w ramach GLES. Aby uniknąć kopiowania z pamięci podczas interakcji z innymi komponentami Androida rozszerzenie dla KHR Images, które umożliwia udostępnianie tablic 2D z danymi obrazów. To rozszerzenie było wymagane na urządzeniach z Androidem od Androida 8.0. graphics-core Biblioteka Androida Jetpacka obejmuje obsługę tworzenia tych obrazów w kodzie zarządzanym i mapowaniu je do przydzielonego HardwareBuffer:

val outputBuffers = Array(numberOfOutputImages) {
  HardwareBuffer.create(
    width, height, HardwareBuffer.RGBA_8888, 1,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
  )
}
val outputEGLImages = Array(numberOfOutputImages) { i ->
    androidx.opengl.EGLExt.eglCreateImageFromHardwareBuffer(
        display,
        outputBuffers[i]
    )!!
}

Nie powoduje to tworzenia stałej tekstury pamięci masowej wymaganej dla program do cieniowania obliczeniowego, który zapisuje bezpośrednio w buforze. W przykładzie użyto glCopyTexSubImage2D, aby skopiować zastosowaną teksturę pamięci masowej przez program do cieniowania obliczeniowego w KHR Image. Jeśli sterownik OpenGL obsługuje EGL Image Storage, a potem to rozszerzenie. można użyć do utworzenia tekstury współdzielonej pamięci stałej, aby uniknąć kopiowania.

Konwersja na cieniowanie obliczeniowe GLSL

Skrypty RenderScript są konwertowane na algorytmy przetwarzania danych GLSL.

Napisz program do cieniowania obliczeń w GLSL

W OpenGL ES Język cieniowania OpenGL (GLSL).

Dostosowanie globalnych skryptów

Ze względu na charakterystyczne cechy skryptu możesz użyć albo jednego, lub jednolitego bufora dla globalnych elementów, które nie są modyfikowane w ramach cieniowania:

  • Jednolity bufor: Zalecane w przypadku często zmienianych globalnych globalnych grup skryptów o rozmiarach większych niż stałego limitu wypychania.

W przypadku globalnych zmian w cieniowaniu możesz użyć parametru zmienna tekstura pamięci masowej lub Obiekt buforowania pamięci Shader.

Wykonywanie obliczeń

Moduły do cieniowania obliczeniowej nie są częścią potoku grafiki. mają charakter ogólny i wykorzystuje możliwości obliczania równoległych zadań. Dzięki temu możesz: większą kontrolę nad sposobem ich wykonywania, ale oznacza również konieczność i lepiej zrozumieć, jak wygląda równoległe wykonywanie zadania.

Utwórz i zainicjuj program obliczeniowy

Tworzenie i inicjowanie programu obliczeniowego ma wiele wspólnego z współpracy z dowolnym innym programem do cieniowania GLES.

  1. Utwórz program i powiązany z nim program do cieniowania obliczeniowego.

  2. Podłącz źródło cieniowania, skompiluj go (i sprawdź wyniki) danej kompilacji).

  3. Podłącz cieniowanie, połącz program i korzystaj z niego.

  4. Utwórz, zainicjuj i wiąż dowolne uniformy.

Rozpocznij obliczenia

Mechanizmy cieniowania obliczeniowe działają w abstrakcyjnej przestrzeni 1D, 2D lub 3D w serii zdefiniowanych w kodzie źródłowym funkcji cieniowania grup roboczych do minimalnego rozmiaru wywołania i geometrii cieniowania. Następujący program do cieniowania działa na obrazie 2D i definiuje grupy robocze na 2 grupy wymiary:

private const val WORKGROUP_SIZE_X = 8
private const val WORKGROUP_SIZE_Y = 8
private const val ROTATION_MATRIX_SHADER =
    """#version 310 es
    layout (local_size_x = $WORKGROUP_SIZE_X, local_size_y = $WORKGROUP_SIZE_Y, local_size_z = 1) in;

Grupy robocze mogą współdzielić wspomnienie (zgodnie z definicją w GL_MAX_COMPUTE_SHARED_MEMORY_SIZE), co najmniej 32 KB i może użyć narzędzia memoryBarrierShared(), aby zapewnić spójny dostęp do pamięci.

Określ rozmiar grupy roboczej

Nawet jeśli obszar problematyczny dobrze sprawdza się w grupach roboczych o wielkości 1, rozmiar grupy roboczej jest istotny, ponieważ pozwala on równolegle działać do cieniowania obliczeniowego. Jeśli rozmiar jest zbyt mały, sterownik GPU może nie wykonywać równoległego przetwarzania obliczeń do końca. W idealnej sytuacji te rozmiary powinny być dostosowane do procesora graficznego, rozsądne ustawienia domyślne działają wystarczająco dobrze na obecnych urządzeniach, takich jak grupa robocza rozmiaru 8 x 8 w kodzie do cieniowania.

Występuje GL_MAX_COMPUTE_WORK_GROUP_COUNT, ale jest znaczny. to musi być co najmniej 65 535 we wszystkich 3 osiach zgodnie ze specyfikacją.

Wyślij cieniowanie

Ostatnim krokiem przy wykonywaniu obliczeń jest wysłanie cieniowania za pomocą z funkcji wysyłki, takich jak glDispatchCompute Funkcja wysyłania Aby ustawić liczbę grup roboczych dla każdej osi:

GLES31.glDispatchCompute(
  roundUp(inputImage.width, WORKGROUP_SIZE_X),
  roundUp(inputImage.height, WORKGROUP_SIZE_Y),
  1 // Z workgroup size. 1 == only one z level, which indicates a 2D kernel
)

Aby zwrócić tę wartość, poczekaj na zakończenie operacji obliczeniowej, korzystając z bariera pamięci:

GLES31.glMemoryBarrier(GLES31.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)

Aby połączyć kilka jąder, (na przykład migrację kodu za pomocą ScriptGroup) tworzenie i wysyłanie wiele programów i synchronizować ich dostęp do danych wyjściowych za pomocą pamięci; wiele barier.

Przykładowa aplikacja i przedstawia 2 zadania obliczeniowe:

  • Rotacja HUE: zadanie obliczeniowe z jednym cieniowaniem obliczeń. Zobacz GLSLImageProcessor::rotateHue.
  • Rozmycie: bardziej złożone zadanie obliczeniowe, które sekwencyjnie wykonuje 2 obliczenia obliczeniowe programy do cieniowania. Przykładowy kod znajdziesz na GLSLImageProcessor::blur.

Więcej informacji o barierach pamięci znajdziesz tutaj: Zapewnianie widoczności , a także Udostępnione zmienne ,