Bug 831624 - Use fb/gralloc to render boot animation, r=cjones,joe,glennrp

This commit is contained in:
Michael Wu 2013-01-24 21:18:30 -08:00
parent c5984d50e5
commit 0cac9879e7
3 changed files with 90 additions and 177 deletions

View File

@ -13,10 +13,6 @@
* limitations under the License.
*/
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <algorithm>
#include <endian.h>
#include <fcntl.h>
@ -32,6 +28,7 @@
#include "android/log.h"
#include "ui/FramebufferNativeWindow.h"
#include "hardware_legacy/power.h"
#include "hardware/gralloc.h"
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args)
@ -232,9 +229,10 @@ public:
struct AnimationFrame {
char path[256];
char *buf;
uint16_t width;
uint16_t height;
const local_file_header *file;
uint32_t width;
uint32_t height;
uint16_t bytepp;
AnimationFrame() : buf(nullptr) {}
AnimationFrame(const AnimationFrame &frame) : buf(nullptr) {
@ -252,7 +250,7 @@ struct AnimationFrame {
return strcmp(path, other.path) < 0;
}
void ReadPngFrame();
void ReadPngFrame(int outputFormat);
};
struct AnimationPart {
@ -281,8 +279,20 @@ RawReader(png_structp png_ptr, png_bytep data, png_size_t length)
state->offset += length;
}
static void
TransformTo565(png_structp png_ptr, png_row_infop row_info, png_bytep data)
{
uint16_t *outbuf = (uint16_t *)data;
uint8_t *inbuf = (uint8_t *)data;
for (int i = 0; i < row_info->rowbytes; i += 3) {
*outbuf++ = ((inbuf[i] & 0xF8) << 8) |
((inbuf[i + 1] & 0xFC) << 3) |
((inbuf[i + 2] ) >> 3);
}
}
void
AnimationFrame::ReadPngFrame()
AnimationFrame::ReadPngFrame(int outputFormat)
{
png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr, nullptr, nullptr);
@ -302,52 +312,45 @@ AnimationFrame::ReadPngFrame()
width = png_get_image_width(pngread, pnginfo);
height = png_get_image_height(pngread, pnginfo);
buf = (char *)malloc(width * height * 3);
switch (outputFormat) {
case HAL_PIXEL_FORMAT_BGRA_8888:
png_set_bgr(pngread);
// FALL THROUGH
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
bytepp = 4;
png_set_filler(pngread, 0xFF, PNG_FILLER_AFTER);
break;
case HAL_PIXEL_FORMAT_RGB_888:
bytepp = 3;
png_set_strip_alpha(pngread);
break;
default:
LOGW("Unknown pixel format %d. Assuming RGB 565.", outputFormat);
// FALL THROUGH
case HAL_PIXEL_FORMAT_RGB_565:
bytepp = 2;
png_set_strip_alpha(pngread);
png_set_read_user_transform_fn(pngread, TransformTo565);
break;
}
// An extra row is added to give libpng enough space when
// decoding 3/4 bytepp inputs for 2 bytepp output surfaces
buf = (char *)malloc(width * (height + 1) * bytepp);
vector<char *> rows(height + 1);
uint32_t stride = width * 3;
uint32_t stride = width * bytepp;
for (int i = 0; i < height; i++) {
rows[i] = buf + (stride * i);
}
rows[height] = nullptr;
png_set_strip_16(pngread);
png_set_palette_to_rgb(pngread);
png_read_image(pngread, (png_bytepp)&rows.front());
png_destroy_read_struct(&pngread, &pnginfo, nullptr);
}
static const EGLint kEGLConfigAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
static bool
CreateConfig(EGLConfig* aConfig, EGLDisplay display, int format)
{
EGLConfig configs[64];
EGLint ncfg = ArrayLength(configs);
if (!eglChooseConfig(display, kEGLConfigAttribs,
configs, ncfg, &ncfg) ||
ncfg < 1) {
return false;
}
for (int j = 0; j < ncfg; ++j) {
EGLConfig config = configs[j];
EGLint id;
if (eglGetConfigAttrib(display, config,
EGL_NATIVE_VISUAL_ID, &id) &&
id > 0 && id == format)
{
*aConfig = config;
return true;
}
}
return false;
}
static void *
AnimationThread(void *)
{
@ -357,21 +360,6 @@ AnimationThread(void *)
return nullptr;
}
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, nullptr, nullptr);
int format;
ANativeWindow const * const window = gNativeWindow.get();
window->query(window, NATIVE_WINDOW_FORMAT, &format);
EGLConfig config;
if (!CreateConfig(&config, display, format)) {
LOGW("Could not find config for pixel format");
return nullptr;
}
EGLSurface surface = eglCreateWindowSurface(display, config, gNativeWindow.get(), nullptr);
const cdir_entry *entry = nullptr;
const local_file_header *file = nullptr;
while ((entry = reader.GetNextEntry(entry))) {
@ -387,6 +375,18 @@ AnimationThread(void *)
return nullptr;
}
int format;
ANativeWindow *window = gNativeWindow.get();
window->query(window, NATIVE_WINDOW_FORMAT, &format);
hw_module_t const *module;
if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) {
LOGW("Could not get gralloc module");
return nullptr;
}
gralloc_module_t const *grmodule =
reinterpret_cast<gralloc_module_t const*>(module);
string descCopy;
descCopy.append(file->GetData(), entry->GetDataSize());
int32_t width, height, fps;
@ -453,104 +453,6 @@ AnimationThread(void *)
sort(part.frames.begin(), part.frames.end());
}
static EGLint gContextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE, 0
};
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, gContextAttribs);
eglMakeCurrent(display, surface, surface, context);
glEnable(GL_TEXTURE_2D);
const char *vsString =
"attribute vec2 aPosition; "
"attribute vec2 aTexCoord; "
"varying vec2 vTexCoord; "
"void main() { "
" gl_Position = vec4(aPosition, 0.0, 1.0); "
" vTexCoord = aTexCoord; "
"}";
const char *fsString =
"precision mediump float; "
"varying vec2 vTexCoord; "
"uniform sampler2D sTexture; "
"void main() { "
" gl_FragColor = vec4(texture2D(sTexture, vTexCoord).rgb, 1.0); "
"}";
GLint status;
GLuint vsh = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vsh, 1, &vsString, nullptr);
glCompileShader(vsh);
glGetShaderiv(vsh, GL_COMPILE_STATUS, &status);
if (!status) {
LOGE("Failed to compile vertex shader");
return nullptr;
}
GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fsh, 1, &fsString, nullptr);
glCompileShader(fsh);
glGetShaderiv(fsh, GL_COMPILE_STATUS, &status);
if (!status) {
LOGE("Failed to compile fragment shader");
return nullptr;
}
GLuint programId = glCreateProgram();
glAttachShader(programId, vsh);
glAttachShader(programId, fsh);
glLinkProgram(programId);
glGetProgramiv(programId, GL_LINK_STATUS, &status);
if (!status) {
LOG("Failed to link program");
return nullptr;
}
GLint positionLoc = glGetAttribLocation(programId, "aPosition");
GLint texCoordLoc = glGetAttribLocation(programId, "aTexCoord");
GLint textureLoc = glGetUniformLocation(programId, "sTexture");
glUseProgram(programId);
GLfloat texCoords[] = { 0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f };
GLfloat vCoords[] = { -1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, -1.0f,
1.0f, 1.0f };
GLuint rectBuf, texBuf;
glGenBuffers(1, &rectBuf);
glGenBuffers(1, &texBuf);
GLuint tex;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEnableVertexAttribArray(positionLoc);
glBindBuffer(GL_ARRAY_BUFFER, rectBuf);
glBufferData(GL_ARRAY_BUFFER, sizeof(vCoords), vCoords, GL_STATIC_DRAW);
glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(texCoordLoc);
glBindBuffer(GL_ARRAY_BUFFER, texBuf);
glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUniform1i(textureLoc, 0);
uint32_t frameDelayUs = 1000000 / fps;
for (uint32_t i = 0; i < parts.size(); i++) {
@ -563,13 +465,33 @@ AnimationThread(void *)
gettimeofday(&tv1, nullptr);
AnimationFrame &frame = part.frames[k];
if (!frame.buf) {
frame.ReadPngFrame();
frame.ReadPngFrame(format);
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
frame.width, frame.height, 0,
GL_RGB, GL_UNSIGNED_BYTE, frame.buf);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
ANativeWindowBuffer *buf;
if (window->dequeueBuffer(window, &buf)) {
LOGW("Failed to get an ANativeWindowBuffer");
break;
}
if (window->lockBuffer(window, buf)) {
LOGW("Failed to lock ANativeWindowBuffer");
window->queueBuffer(window, buf);
break;
}
void *vaddr;
if (grmodule->lock(grmodule, buf->handle,
GRALLOC_USAGE_SW_READ_NEVER |
GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_FB,
0, 0, width, height, &vaddr)) {
LOGW("Failed to lock buffer_handle_t");
window->queueBuffer(window, buf);
break;
}
memcpy(vaddr, frame.buf,
frame.width * frame.height * frame.bytepp);
grmodule->unlock(grmodule, buf->handle);
gettimeofday(&tv2, nullptr);
@ -581,7 +503,7 @@ AnimationThread(void *)
LOGW("Frame delay is %d us but decoding took %d us", frameDelayUs, tv2.tv_usec);
}
eglSwapBuffers(display, surface);
window->queueBuffer(window, buf);
if (part.count && j >= part.count) {
free(frame.buf);
@ -591,19 +513,7 @@ AnimationThread(void *)
usleep(frameDelayUs * part.pause);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glDeleteTextures(1, &tex);
glDeleteBuffers(1, &texBuf);
glDeleteBuffers(1, &rectBuf);
glDeleteProgram(programId);
glDeleteShader(fsh);
glDeleteShader(vsh);
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(display, context);
eglDestroySurface(display, surface);
return nullptr;
}

View File

@ -27,8 +27,6 @@ CPPSRCS = nsBrowserApp.cpp
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += BootAnimation.cpp
LIBS += \
-lGLESv2 \
-lEGL \
-lui \
-lhardware_legacy \
-lhardware \

View File

@ -45,9 +45,14 @@
/* necessary for boot animation code */
#ifdef MOZ_WIDGET_GONK
#define PNG_SEQUENTIAL_READ_SUPPORTED
#define PNG_EASY_ACCESS_SUPPORTED
#define PNG_READ_BGR_SUPPORTED
#define PNG_READ_EXPAND_SUPPORTED
#define PNG_READ_FILLER_SUPPORTED
#define PNG_READ_STRIP_16_TO_8_SUPPORTED
#define PNG_READ_STRIP_ALPHA_SUPPORTED
#define PNG_READ_USER_TRANSFORM_SUPPORTED
#define PNG_SEQUENTIAL_READ_SUPPORTED
#endif
#ifdef MOZ_PNG_WRITE