La mia applicazione deve eseguire alcune elaborazioni su fotogrammi videocamera dal vivo sulla CPU, prima di renderle sulla GPU. Ci sono anche altre cose che vengono renderizzate sulla GPU che dipende dai risultati dell'elaborazione della CPU; quindi è importante mantenere tutto sincronizzato in modo da non rendere il frame stesso sulla GPU finché non sono disponibili anche i risultati dell'elaborazione della CPU per quel frame.Telecamera da soffitto più bassa per approccio da CPU a GPU su Android
La domanda è qual è l'approccio di overhead più basso per questo su Android?
L'elaborazione della CPU nel mio caso richiede solo un'immagine in scala di grigi, quindi un formato YUV in cui è imballato il piano Y è l'ideale (e tende ad essere una buona corrispondenza con il formato nativo dei dispositivi della fotocamera). NV12, NV21 o YUV completamente planare forniranno tutti un accesso low-overhead ideale a scala di grigi, quindi sarebbe preferibile sul lato CPU.
Nell'API della telecamera originale il setPreviewCallbackWithBuffer() era l'unico modo ragionevole per ottenere dati nella CPU per l'elaborazione. Questo aveva il piano Y separato, quindi era l'ideale per l'elaborazione della CPU. Rendere questo frame disponibile per OpenGL per il rendering in un basso overhead era l'aspetto più impegnativo. Alla fine ho scritto una routine di conversione del colore NEON per emettere RGB565 e usare semplicemente glTexSubImage2d per renderlo disponibile sulla GPU. Questo è stato implementato per la prima volta nel periodo Nexus 1, in cui anche una chiamata glTexSubImage2d di 320x240 ha impiegato 50 ms di tempo CPU (presumibilmente i driver poveri tentano di fare il swizzling della trama - questo è stato migliorato in modo significativo in un aggiornamento del sistema in seguito).
Nel corso della giornata ho esaminato le estensioni di eglImage, ma non sembrano disponibili o sufficientemente documentate per le app degli utenti. Ho dato un piccolo sguardo alle classi interne di GraphicsBuffer Android, ma idealmente voglio rimanere nel mondo delle API pubbliche supportate.
L'API android.hardware.camera2 era promettente per poter collegare sia ImageReader che SurfaceTexture a una sessione di acquisizione. Sfortunatamente non riesco a vedere alcun modo di garantire la giusta pipeline sequenziale qui: tenere fuori chiamata updateTexImage() finché la CPU non è stata elaborata è abbastanza facile, ma se un altro frame è arrivato durante tale elaborazione, updateTexImage() salterà direttamente all'ultimo telaio. Sembra anche con più uscite ci saranno copie indipendenti dei frame in ciascuna delle code che idealmente vorrei evitare.
Idealmente questo è ciò che vorrei:
- Driver fotocamera riempie certa memoria con le ultime telaio
- CPU ottiene puntatore ai dati in memoria, può leggere i dati Y senza una copia compiuti
- CPU gestisce dati e imposta un flag nel mio codice, quando telaio è pronto
- quando si inizia a rendere un telaio, controllare se un nuovo telaio è pronto
- chiamata alcune API di impegnare la stessa memoria come GL trama
- Quando un frame più recente è pronto, rilasciare il buffer tenendo il fotogramma precedente indietro nella piscina
non riesco a vedere un modo di fare esattamente questo stile di zero-copy con API pubblica su Android, ma ciò che è il più vicino che è possibile ottenere?
Una cosa pazzesca che ho provato sembra funzionare, ma non è documentata: l'API NDK ANativeWindow può accettare il formato di dati NV12, anche se la costante di formato appropriata non è tra quelle presenti nelle intestazioni pubbliche.Ciò consente a SurfaceTexture di essere riempito con i dati NV12 da memcpy() per evitare la conversione del colore sul lato CPU e qualsiasi swizzling che si verifica lato driver in glTexImage2d. Questa è ancora una copia extra dei dati, anche se sembra che non dovrebbe essere necessaria, e ancora una volta in quanto non documentata potrebbe non funzionare su tutti i dispositivi. Una fotocamera sequenziale a copia zero supportata -> ImageReader -> SurfaceTexture o equivalente sarebbe perfetta.
Ottima risposta, grazie. Il lato CPU in realtà non sta scrivendo sul buffer, è fondamentalmente un'app AR che ha bisogno di calcolare il movimento dell'oggetto nel frame sulla CPU per rendere correttamente il contenuto sovrapposto nella giusta posizione sulla GPU. Quindi i percorsi paralleli andrebbero bene, ma penso che la parte che manca sia ottenere il buffer giusto in SurfaceTexture; la "ultima CPU elaborata" anziché "l'ultima in coda". – tangobravo
Ho esaminato i documenti RenderScript un po 'ieri. Dubito che funzionerà, ma se è possibile avere un'allocazione che è USAGE_IO_INPUT | USAGE_IO_OUTPUT | USAGE_SCRIPT che potrebbe consentire di essere sia la destinazione per la telecamera sia la sorgente per SurfaceTexture, con la CPU in controllo della temporizzazione del buffer che si muove attraverso la pipe. Le intestazioni NDK di RenderScript hanno una funzione getPointer() sull'allocazione, quindi dovrebbe essere in grado di elaborare in codice C++ NDK (sfortunatamente le intestazioni non sono nell'ultima versione di NDK, quindi non si è sicuri di quale sia il problema). – tangobravo
Non ho una buona immagine mentale della tua pipeline parallela, ma sembra che tu voglia chiamare 'updateTexImage()' e 'acquireLatestImage()' allo stesso tempo, e poi semplicemente sederti sul buffer latched mentre la CPU fa il suo lavoro. Non è esente da corse, in quanto una nuova cornice potrebbe atterrare tra le due chiamate, ma ciò dovrebbe essere raro e può essere mitigato tenendo d'occhio l'orologio. – fadden