playsinline - OpenGL para video en iPhone
playsinline video html5 (4)
Actualmente estoy trabajando en un proyecto para convertir una simulación de física en un video en el iPhone.
Para hacer esto, actualmente estoy usando dos bucles diferentes. El primer bucle se ejecuta en el bloque donde el objeto AVAssetWriterInput sondea el EAGLView para obtener más imágenes. EAGLView proporciona las imágenes de una matriz donde se almacenan.
El otro ciclo es la simulación real. Apagué el temporizador de simulación y llamo a la marca yo mismo con una diferencia de tiempo preestablecida cada vez. Cada vez que se llama a un tic, creo una nueva imagen en el método de buffer de intercambio de EAGLView después de que los buffers han sido intercambiados. Esta imagen luego se coloca en la matriz que AVAssetWriter sondea.
También hay algún código misceláneo para asegurarse de que la matriz no se vuelva demasiado grande
Todo esto funciona bien, pero es muy, muy lento.
¿Hay algo que esté haciendo que, conceptualmente, haga que todo el proceso sea más lento de lo que podría ser? Además, ¿alguien sabe de una forma más rápida de obtener una imagen de Open GL que glReadPixels?
La memoria de video está diseñada para que sea rápida para escribir y lenta para leer. Es por eso que realizo renderizado a textura. Este es el método completo que he creado para representar la escena en textura (hay algunos contenedores personalizados, pero creo que es bastante sencillo reemplazarlos por los suyos):
-(TextureInf*) makeSceneSnapshot {
// create texture frame buffer
GLuint textureFrameBuffer, sceneRenderTexture;
glGenFramebuffersOES(1, &textureFrameBuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);
// create texture to render scene to
glGenTextures(1, &sceneRenderTexture);
glBindTexture(GL_TEXTURE_2D, sceneRenderTexture);
// create TextureInf object
TextureInf* new_texture = new TextureInf();
new_texture->setTextureID(sceneRenderTexture);
new_texture->real_width = [self viewportWidth];
new_texture->real_height = [self viewportHeight];
//make sure the texture dimensions are power of 2
new_texture->width = cast_to_power(new_texture->real_width, 2);
new_texture->height = cast_to_power(new_texture->real_height, 2);
//AABB2 = axis aligned bounding box (2D)
AABB2 tex_box;
tex_box.p1.x = 1 - (GLfloat)new_texture->real_width / (GLfloat)new_texture->width;
tex_box.p1.y = 0;
tex_box.p2.x = 1;
tex_box.p2.y = (GLfloat)new_texture->real_height / (GLfloat)new_texture->height;
new_texture->setTextureBox(tex_box);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, new_texture->width, new_texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, sceneRenderTexture, 0);
// check for completness
if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
new_texture->release();
@throw [NSException exceptionWithName: EXCEPTION_NAME
reason: [NSString stringWithFormat: @"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)]
userInfo: nil];
new_texture = nil;
} else {
// render to texture
[self renderOneFrame];
}
glDeleteFramebuffersOES(1, &textureFrameBuffer);
//restore default frame and render buffers
glBindFramebufferOES(GL_FRAMEBUFFER_OES, _defaultFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, _colorRenderbuffer);
glEnable(GL_BLEND);
[self updateViewport];
glMatrixMode(GL_MODELVIEW);
return new_texture;
}
Por supuesto, si está haciendo snapshots todo el tiempo, será mejor que cree un frame de textura y renderice búferes solo una vez (y asigne memoria para ellos).
Una cosa para recordar es que la GPU se ejecuta de forma asíncrona desde la CPU, por lo que si intentas hacer glReadPixels inmediatamente después de terminar de renderizar, tendrás que esperar a que los comandos se descarguen a la GPU y se procesen para poder volver a leerlos. .
En lugar de esperar sincrónicamente, renderice instantáneas en una cola de texturas (usando FBO como Max mencionado). Espera hasta que hayas renderizado un par de cuadros más antes de eliminar uno de los cuadros anteriores. No sé si el iPhone admite vallas o sincroniza objetos, pero si es así, puede verificarlos para ver si el renderizado finalizó antes de leer los píxeles.
Podría intentar usar un objeto CADisplayLink para asegurarse de que su velocidad de dibujo y su tasa de captura correspondan a la frecuencia de actualización de la pantalla del dispositivo. Es posible que esté ralentizando el tiempo de ejecución del ciclo de ejecución al actualizar y capturar demasiadas veces por actualización de pantalla del dispositivo.
Dependiendo de los objetivos de su aplicación, puede que no sea necesario que capture cada fotograma que presente, de modo que en su selector, puede elegir si desea capturar el fotograma actual.
Si bien la pregunta no es nueva, todavía no está respondida, así que pensé en colaborar.
glReadPixels es muy lento y, por lo tanto, no se puede utilizar para grabar video desde una aplicación OpenGL sin afectar negativamente el rendimiento.
Encontramos una solución temporal y hemos creado un SDK gratuito llamado Everyplay que puede grabar gráficos basados en OpenGL en un archivo de video, sin pérdida de rendimiento. Puede verificarlo en https://developers.everyplay.com/