Pipeline de processamento e renderização de câmera com cópia zero no Android

Preciso fazer um processo somente leitura do CPU nos dados da câmera ao vivo (apenas do plano Y), seguido pela renderização na GPU. Os quadros não devem ser renderizados até que o processamento seja concluído (por isso nem sempre quero renderizar o quadro mais recente da câmera, apenas o mais recente que o lado da CPU concluiu o processamento). A renderização é dissociada do processamento da câmera e visa a 60 FPS, mesmo que os quadros da câmera cheguem a uma taxa mais baixa do que isso.

Há uma pergunta relacionada, mas de nível superior, em:Menor câmera aérea para abordagem de CPU para GPU no android

Para descrever a configuração atual com um pouco mais de detalhes: temos um buffer pool no lado do aplicativo para dados da câmera em que os buffers são "livres", "na exibição" ou "exibição pendente". Quando um novo quadro da câmera chega, pegamos um buffer livre, armazenamos o quadro (ou uma referência a ele, se os dados reais estiverem em algum pool de buffers fornecido pelo sistema) lá, faça o processamento e armazene os resultados no buffer, depois defina o buffer "exibição pendente". No thread do renderizador, se houver algum buffer "exibição pendente" no início do loop de renderização, trancamos para que ele seja o "em exibição", renderize a câmera e renderize o outro conteúdo usando as informações processadas calculadas a partir da mesma quadro da câmera.

Graças à resposta de @ fadden na pergunta acima, agora entendo o recurso "saída paralela" da API da câmera android2 compartilha os buffers entre as várias filas de saída, portanto, não envolva cópias nos dados, pelo menos no android moderno.

Em um comentário, havia uma sugestão de que eu poderia travar as saídas SurfaceTexture e ImageReader ao mesmo tempo e apenas "sentar no buffer" até que o processamento estivesse concluído. Infelizmente, acho que isso não é aplicável no meu caso, devido à renderização dissociada que ainda queremos conduzir a 60 FPS e que ainda precisará de acesso ao quadro anterior enquanto o novo está sendo processado para garantir que as coisas não aconteçam. fora de sincronia.

Uma solução que veio à mente é ter várias SurfaceTextures - uma em cada um dos buffers do lado do aplicativo (atualmente usamos 3). Com esse esquema, quando obtemos um novo quadro de câmera, obteríamos um buffer gratuito do nosso pool de aplicativos. Então nós chamamosacquireLatestImage() em um ImageReader para obter os dados para processamento e chameupdateTexImage() no SurfaceTexture no buffer livre. No momento da renderização, precisamos apenas garantir que o SufaceTexture do buffer "em exibição" seja o vinculado ao GL e que tudo esteja sincronizado a maior parte do tempo (como @fadden comentou, há uma corrida entre chamar oupdateTexImage() eacquireLatestImage() mas essa janela de tempo deve ser pequena o suficiente para torná-la rara e talvez seja detectável e corrigível de qualquer maneira usando os carimbos de data e hora nos buffers).

Percebo nos documentos queupdateTexImage() só pode ser chamado quando o SurfaceTexture está vinculado a um contexto GL, o que sugere que eu também precisarei de um contexto GL no thread de processamento da câmera para que o thread da câmera possaupdateTexImage() no SurfaceTexture no buffer "livre" enquanto o thread de renderização ainda é capaz de renderizar a partir do SurfaceTexture a partir do buffer "em exibição".

Então, para as perguntas:

Parece uma abordagem sensata?O SurfaceTextures é basicamente um invólucro leve em torno do buffer pool compartilhado ou consome algum recurso limitado de hardware e deve ser usado com moderação?As chamadas SurfaceTexture são baratas o suficiente para que o uso de várias ainda seja uma grande conquista, apenas copiando os dados?O plano é ter dois threads com contextos GL distintos com um SurfaceTexture diferente vinculado em cada um que provavelmente funcione ou estou pedindo um mundo de drivers de dor e bugs?

Parece promissor o suficiente que eu vou tentar; mas achei que vale a pena perguntar aqui, caso alguém (basicamente @fadden!) conheça detalhes internos que eu ignorei, o que tornaria isso uma má ideia.

questionAnswers(1)

yourAnswerToTheQuestion