Per i carichi di lavoro in cui il calcolo GPU è ideale, esegui la migrazione degli script RenderScript a OpenGL ES (GLES) consente applicazioni scritte in Kotlin, Java o con l'NDK per sfruttare l'hardware della GPU. Di seguito è riportata una panoramica generale che ti aiuterà a utilizzare gli strumenti di computing OpenGL ES 3.1 per sostituire gli script RenderScript.
Inizializzazione GLES
Anziché creare un oggetto di contesto RenderScript, segui questi passaggi per creare un contesto GLES fuori schermo utilizzando EGL:
Imposta la visualizzazione predefinita
Inizializza EGL utilizzando la visualizzazione predefinita, specificando la versione GLES.
Scegli una configurazione EGL con un tipo di superficie
EGL_PBUFFER_BIT
Utilizza Display and Config per creare un contesto EGL.
Crea la superficie fuori schermo con
eglCreatePBufferSurface
Se il contesto verrà utilizzato solo per il calcolo, si tratta di un modello banalmente piccolo (1x1) superficie.Crea il thread di rendering e la chiamata
eglMakeCurrent
nel thread di rendering con il display, la piattaforma ed EGL per associare il contesto GL al thread.
L'app di esempio mostra come inizializzare il contesto GLES in
GLSLImageProcessor.kt
Per saperne di più, vedi
EGLSurfaces e OpenGL ES.
Output debug GLES
Per ricevere errori utili da OpenGL viene utilizzata un'estensione per attivare il logging di debug
che imposta un callback di output di debug. Il metodo per eseguire questa operazione dall'SDK,
glDebugMessageCallbackKHR
, non è mai stato implementato e genera un
eccezione. L'app di esempio
include un wrapper per il callback dal codice NDK.
Allocazioni GLES
È possibile eseguire la migrazione di un'allocazione di RenderScript texture di archiviazione immutabile o una Oggetto buffer di archiviazione Shader. Per le immagini di sola lettura, puoi utilizzare un Oggetto Sampler, che consente dei filtri.
Le risorse GLES vengono allocate all'interno di GLES. Per evitare di copiare la memoria
quando si interagisce con altri componenti Android, c'è
per KHR Images che consente la condivisione
di array 2D di dati immagine. Questa estensione è necessaria per i dispositivi Android
a partire da Android 8.0. La
grafica-core
Libreria Android Jetpack
include il supporto per la creazione di queste immagini all'interno del codice gestito e della mappatura
a una HardwareBuffer
assegnata:
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]
)!!
}
Purtroppo, ciò non crea la texture di archiviazione immutabile necessaria per
in Compute Engine,
in modo che scriva direttamente nel buffer. L'esempio utilizza
glCopyTexSubImage2D
per copiare la texture di archiviazione utilizzata
dal Compute Engine all'interno di KHR Image
. Se il driver OpenGL supporta
EGL Image Storage, poi quella estensione.
può essere usato per creare una texture di archiviazione condivisa immutabile per evitare la copia.
Conversione in GLSL Compute Engine
I tuoi script RenderScript vengono convertiti in compute builder GLSL.
Scrivi uno shaker di computing GLSL
In OpenGL ES,gli streamr dei computing sono scritti in Linguaggio di ombreggiatura OpenGL (GLSL).
Adattamento degli script globali
In base alle caratteristiche dei testi globali dello script, puoi usare le uniformi oppure oggetti buffer uniformi per i globali che non vengono modificati all'interno dello Shaper:
- Buffer uniforme: Consigliato per gli script globali degli script modificati di frequente di dimensioni superiori al il limite costante.
Per i dati globali che vengono modificati all'interno dello streamr, puoi usare texture di archiviazione immutabile o una Oggetto buffer di archiviazione Shader.
Esegui calcoli
I responsabili di computing non fanno parte della pipeline grafica, sono generici e progettato per calcolare job altamente parallelizzabili. In questo modo puoi avere maggiore controllo sulle modalità di esecuzione degli annunci, ma allo stesso tempo significa capire meglio come viene caricato in contemporanea il job.
crea e inizializza il programma di computing
La creazione e l'inizializzazione del programma di computing hanno molto in comune con lavorare con qualsiasi altro shar GLES.
Creare il programma e il computing shaker associato.
Collega il codice sorgente, compila lo shaker (e controlla i risultati) della compilazione).
Allega lo shaker, collega il programma e usalo.
Creare, inizializzare e vincolare eventuali uniformi.
Avvia un calcolo
Gli Shaper Compute operano all'interno di uno spazio 1D, 2D o 3D astratto su una serie di gruppi di lavoro, che sono definiti all'interno del codice sorgente dello streamr e rappresentano la dimensione minima di chiamata e la geometria dello shar. Il seguente shaker lavora su un'immagine 2D e definisce i gruppi di lavoro in due dimensioni:
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;
I gruppi di lavoro possono condividere la memoria, definita da GL_MAX_COMPUTE_SHARED_MEMORY_SIZE
,
che è di almeno 32 kB e può utilizzare memoryBarrierShared()
per fornire
un accesso coerente alla memoria.
Definisci la dimensione del gruppo di lavoro
Anche se lo spazio dedicato ai problemi funziona bene con gruppi di lavoro di dimensioni pari a 1, l'impostazione di un valore è importante creare in contemporanea una dimensione appropriata del gruppo di lavoro per il caricamento in contemporanea. Se la dimensione è troppo piccola, il driver GPU potrebbe non caricare in contemporanea abbastanza, ad esempio. Idealmente, queste dimensioni dovrebbero essere ottimizzate per GPU, anche se valori predefiniti ragionevoli funzionano abbastanza bene sui dispositivi attuali, come il gruppo di lavoro di 8 x 8 nello snippet Shar.
Esiste un GL_MAX_COMPUTE_WORK_GROUP_COUNT
, ma è sostanziale; deve essere
almeno 65535 in tutti e tre gli assi, in base alle specifiche.
Liberati allo Shar
Il passaggio finale nell'esecuzione dei calcoli consiste nell'inviare lo Shader usando uno
delle funzioni di tracciamento, come
glDispatchCompute
La funzione di invio è responsabile
per impostare il numero di gruppi di lavoro per ogni asse:
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
)
Per restituire il valore, attendi il completamento dell'operazione di calcolo utilizzando un barriera della memoria:
GLES31.glMemoryBarrier(GLES31.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)
Per concatenare più kernel,
(ad esempio, per eseguire la migrazione del codice utilizzando ScriptGroup
), crea e invia
più programmi e sincronizza il loro accesso all'output con la memoria
barriere.
L'app di esempio illustra due attività di computing:
- Rotazione HUE: un'attività di computing con un singolo sharder computing. Consulta
GLSLImageProcessor::rotateHue
per l'esempio di codice. - Sfocatura: un'attività di computing più complessa che esegue in modo sequenziale due operazioni
Shaper. Vedi
GLSLImageProcessor::blur
per l'esempio di codice.
Per scoprire di più sulle barriere della memoria, consulta Garanzia di visibilità così come Variabili condivise di Google.