summaryrefslogtreecommitdiffstats
path: root/widget/gonk/libdisplay
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /widget/gonk/libdisplay
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'widget/gonk/libdisplay')
-rw-r--r--widget/gonk/libdisplay/BootAnimation.cpp726
-rw-r--r--widget/gonk/libdisplay/BootAnimation.h30
-rw-r--r--widget/gonk/libdisplay/DisplaySurface.h112
-rw-r--r--widget/gonk/libdisplay/FramebufferSurface.cpp207
-rw-r--r--widget/gonk/libdisplay/FramebufferSurface.h93
-rw-r--r--widget/gonk/libdisplay/GonkDisplay.h91
-rw-r--r--widget/gonk/libdisplay/GonkDisplayJB.cpp461
-rw-r--r--widget/gonk/libdisplay/GonkDisplayJB.h85
-rw-r--r--widget/gonk/libdisplay/GraphicBufferAlloc.cpp53
-rw-r--r--widget/gonk/libdisplay/GraphicBufferAlloc.h44
-rw-r--r--widget/gonk/libdisplay/VirtualDisplaySurface.cpp635
-rw-r--r--widget/gonk/libdisplay/VirtualDisplaySurface.h250
-rw-r--r--widget/gonk/libdisplay/moz.build59
13 files changed, 2846 insertions, 0 deletions
diff --git a/widget/gonk/libdisplay/BootAnimation.cpp b/widget/gonk/libdisplay/BootAnimation.cpp
new file mode 100644
index 000000000..c275179fc
--- /dev/null
+++ b/widget/gonk/libdisplay/BootAnimation.cpp
@@ -0,0 +1,726 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <endian.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <vector>
+#include "mozilla/FileUtils.h"
+#include "png.h"
+
+#include "android/log.h"
+#include "GonkDisplay.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)
+#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args)
+
+using namespace mozilla;
+using namespace std;
+
+static pthread_t sAnimationThread;
+static bool sRunAnimation;
+
+/* See http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
+struct local_file_header {
+ uint32_t signature;
+ uint16_t min_version;
+ uint16_t general_flag;
+ uint16_t compression;
+ uint16_t lastmod_time;
+ uint16_t lastmod_date;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t filename_size;
+ uint16_t extra_field_size;
+ char data[0];
+
+ uint32_t GetDataSize() const
+ {
+ return letoh32(uncompressed_size);
+ }
+
+ uint32_t GetSize() const
+ {
+ /* XXX account for data descriptor */
+ return sizeof(local_file_header) + letoh16(filename_size) +
+ letoh16(extra_field_size) + GetDataSize();
+ }
+
+ const char * GetData() const
+ {
+ return data + letoh16(filename_size) + letoh16(extra_field_size);
+ }
+} __attribute__((__packed__));
+
+struct data_descriptor {
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+} __attribute__((__packed__));
+
+struct cdir_entry {
+ uint32_t signature;
+ uint16_t creator_version;
+ uint16_t min_version;
+ uint16_t general_flag;
+ uint16_t compression;
+ uint16_t lastmod_time;
+ uint16_t lastmod_date;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t filename_size;
+ uint16_t extra_field_size;
+ uint16_t file_comment_size;
+ uint16_t disk_num;
+ uint16_t internal_attr;
+ uint32_t external_attr;
+ uint32_t offset;
+ char data[0];
+
+ uint32_t GetDataSize() const
+ {
+ return letoh32(compressed_size);
+ }
+
+ uint32_t GetSize() const
+ {
+ return sizeof(cdir_entry) + letoh16(filename_size) +
+ letoh16(extra_field_size) + letoh16(file_comment_size);
+ }
+
+ bool Valid() const
+ {
+ return signature == htole32(0x02014b50);
+ }
+} __attribute__((__packed__));
+
+struct cdir_end {
+ uint32_t signature;
+ uint16_t disk_num;
+ uint16_t cdir_disk;
+ uint16_t disk_entries;
+ uint16_t cdir_entries;
+ uint32_t cdir_size;
+ uint32_t cdir_offset;
+ uint16_t comment_size;
+ char comment[0];
+
+ bool Valid() const
+ {
+ return signature == htole32(0x06054b50);
+ }
+} __attribute__((__packed__));
+
+/* We don't have access to libjar and the zip reader in android
+ * doesn't quite fit what we want to do. */
+class ZipReader {
+ const char *mBuf;
+ const cdir_end *mEnd;
+ const char *mCdir_limit;
+ uint32_t mBuflen;
+
+public:
+ ZipReader() : mBuf(nullptr) {}
+ ~ZipReader() {
+ if (mBuf)
+ munmap((void *)mBuf, mBuflen);
+ }
+
+ bool OpenArchive(const char *path)
+ {
+ int fd;
+ do {
+ fd = open(path, O_RDONLY);
+ } while (fd == -1 && errno == EINTR);
+ if (fd == -1)
+ return false;
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1 || sb.st_size < sizeof(cdir_end)) {
+ close(fd);
+ return false;
+ }
+
+ mBuflen = sb.st_size;
+ mBuf = (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (!mBuf) {
+ return false;
+ }
+
+ madvise(mBuf, sb.st_size, MADV_SEQUENTIAL);
+
+ mEnd = (cdir_end *)(mBuf + mBuflen - sizeof(cdir_end));
+ while (!mEnd->Valid() &&
+ (char *)mEnd > mBuf) {
+ mEnd = (cdir_end *)((char *)mEnd - 1);
+ }
+
+ mCdir_limit = mBuf + letoh32(mEnd->cdir_offset) + letoh32(mEnd->cdir_size);
+
+ if (!mEnd->Valid() || mCdir_limit > (char *)mEnd) {
+ munmap((void *)mBuf, mBuflen);
+ mBuf = nullptr;
+ return false;
+ }
+
+ return true;
+ }
+
+ /* Pass null to get the first cdir entry */
+ const cdir_entry * GetNextEntry(const cdir_entry *prev)
+ {
+ const cdir_entry *entry;
+ if (prev)
+ entry = (cdir_entry *)((char *)prev + prev->GetSize());
+ else
+ entry = (cdir_entry *)(mBuf + letoh32(mEnd->cdir_offset));
+
+ if (((char *)entry + entry->GetSize()) > mCdir_limit ||
+ !entry->Valid())
+ return nullptr;
+ return entry;
+ }
+
+ string GetEntryName(const cdir_entry *entry)
+ {
+ uint16_t len = letoh16(entry->filename_size);
+
+ string name;
+ name.append(entry->data, len);
+ return name;
+ }
+
+ const local_file_header * GetLocalEntry(const cdir_entry *entry)
+ {
+ const local_file_header * data =
+ (local_file_header *)(mBuf + letoh32(entry->offset));
+ if (((char *)data + data->GetSize()) > (char *)mEnd)
+ return nullptr;
+ return data;
+ }
+};
+
+struct AnimationFrame {
+ char path[256];
+ png_color_16 bgcolor;
+ char *buf;
+ const local_file_header *file;
+ uint32_t width;
+ uint32_t height;
+ uint16_t bytepp;
+ bool has_bgcolor;
+
+ AnimationFrame() : buf(nullptr) {}
+ AnimationFrame(const AnimationFrame &frame) : buf(nullptr) {
+ strncpy(path, frame.path, sizeof(path));
+ file = frame.file;
+ }
+ ~AnimationFrame()
+ {
+ if (buf)
+ free(buf);
+ }
+
+ bool operator<(const AnimationFrame &other) const
+ {
+ return strcmp(path, other.path) < 0;
+ }
+
+ void ReadPngFrame(int outputFormat);
+};
+
+struct AnimationPart {
+ int32_t count;
+ int32_t pause;
+ // If you alter the size of the path, change ReadFromString() as well.
+ char path[256];
+ vector<AnimationFrame> frames;
+
+ bool
+ ReadFromString(const char* aLine)
+ {
+ MOZ_ASSERT(aLine);
+ // this 255 value must be in sync with AnimationPart::path.
+ return sscanf(aLine, "p %d %d %255s", &count, &pause, path) == 3;
+ }
+};
+
+struct RawReadState {
+ const char *start;
+ uint32_t offset;
+ uint32_t length;
+};
+
+static void
+RawReader(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ RawReadState *state = (RawReadState *)png_get_io_ptr(png_ptr);
+ if (length > (state->length - state->offset))
+ png_error(png_ptr, "PNG read overrun");
+
+ memcpy(data, state->start + state->offset, 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 (uint32_t i = 0; i < row_info->rowbytes; i += 3) {
+ *outbuf++ = ((inbuf[i] & 0xF8) << 8) |
+ ((inbuf[i + 1] & 0xFC) << 3) |
+ ((inbuf[i + 2] ) >> 3);
+ }
+}
+
+static uint16_t
+GetFormatBPP(int aFormat)
+{
+ uint16_t bpp = 0;
+
+ switch (aFormat) {
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ bpp = 4;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ bpp = 3;
+ break;
+ default:
+ LOGW("Unknown pixel format %d. Assuming RGB 565.", aFormat);
+ // FALL THROUGH
+ case HAL_PIXEL_FORMAT_RGB_565:
+ bpp = 2;
+ break;
+ }
+
+ return bpp;
+}
+
+void
+AnimationFrame::ReadPngFrame(int outputFormat)
+{
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ static const png_byte unused_chunks[] =
+ { 99, 72, 82, 77, '\0', /* cHRM */
+ 104, 73, 83, 84, '\0', /* hIST */
+ 105, 67, 67, 80, '\0', /* iCCP */
+ 105, 84, 88, 116, '\0', /* iTXt */
+ 111, 70, 70, 115, '\0', /* oFFs */
+ 112, 67, 65, 76, '\0', /* pCAL */
+ 115, 67, 65, 76, '\0', /* sCAL */
+ 112, 72, 89, 115, '\0', /* pHYs */
+ 115, 66, 73, 84, '\0', /* sBIT */
+ 115, 80, 76, 84, '\0', /* sPLT */
+ 116, 69, 88, 116, '\0', /* tEXt */
+ 116, 73, 77, 69, '\0', /* tIME */
+ 122, 84, 88, 116, '\0'}; /* zTXt */
+ static const png_byte tRNS_chunk[] =
+ {116, 82, 78, 83, '\0'}; /* tRNS */
+#endif
+
+ png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ nullptr, nullptr, nullptr);
+
+ if (!pngread)
+ return;
+
+ png_infop pnginfo = png_create_info_struct(pngread);
+
+ if (!pnginfo) {
+ png_destroy_read_struct(&pngread, &pnginfo, nullptr);
+ return;
+ }
+
+ if (setjmp(png_jmpbuf(pngread))) {
+ // libpng reported an error and longjumped here. Clean up and return.
+ png_destroy_read_struct(&pngread, &pnginfo, nullptr);
+ return;
+ }
+
+ RawReadState state;
+ state.start = file->GetData();
+ state.length = file->GetDataSize();
+ state.offset = 0;
+
+ png_set_read_fn(pngread, &state, RawReader);
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ /* Ignore unused chunks */
+ png_set_keep_unknown_chunks(pngread, 1, unused_chunks,
+ (int)sizeof(unused_chunks)/5);
+
+ /* Ignore the tRNS chunk if we only want opaque output */
+ if (outputFormat == HAL_PIXEL_FORMAT_RGB_888 ||
+ outputFormat == HAL_PIXEL_FORMAT_RGB_565) {
+ png_set_keep_unknown_chunks(pngread, 1, tRNS_chunk, 1);
+ }
+#endif
+
+ png_read_info(pngread, pnginfo);
+
+ png_color_16p colorp;
+ has_bgcolor = (PNG_INFO_bKGD == png_get_bKGD(pngread, pnginfo, &colorp));
+ bgcolor = has_bgcolor ? *colorp : png_color_16();
+ width = png_get_image_width(pngread, pnginfo);
+ height = png_get_image_height(pngread, pnginfo);
+
+ LOG("Decoded %s: %d x %d frame with bgcolor? %s (%#x, %#x, %#x; gray:%#x)",
+ path, width, height, has_bgcolor ? "yes" : "no",
+ bgcolor.red, bgcolor.green, bgcolor.blue, bgcolor.gray);
+
+ bytepp = GetFormatBPP(outputFormat);
+
+ 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:
+ png_set_filler(pngread, 0xFF, PNG_FILLER_AFTER);
+ break;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ png_set_strip_alpha(pngread);
+ break;
+ default:
+ LOGW("Unknown pixel format %d. Assuming RGB 565.", outputFormat);
+ // FALL THROUGH
+ case HAL_PIXEL_FORMAT_RGB_565:
+ 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 * bytepp;
+ for (uint32_t 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_set_gray_to_rgb(pngread);
+ png_read_image(pngread, (png_bytepp)&rows.front());
+ png_destroy_read_struct(&pngread, &pnginfo, nullptr);
+}
+
+/**
+ * Return a wchar_t that when used to |wmemset()| an image buffer will
+ * fill it with the color defined by |color16|. The packed wchar_t
+ * may comprise one or two pixels depending on |outputFormat|.
+ */
+static wchar_t
+AsBackgroundFill(const png_color_16& color16, int outputFormat)
+{
+ static_assert(sizeof(wchar_t) == sizeof(uint32_t),
+ "TODO: support 2-byte wchar_t");
+ union {
+ uint32_t r8g8b8;
+ struct {
+ uint8_t b8;
+ uint8_t g8;
+ uint8_t r8;
+ uint8_t x8;
+ };
+ } color;
+ color.b8 = color16.blue;
+ color.g8 = color16.green;
+ color.r8 = color16.red;
+ color.x8 = 0xFF;
+
+ switch (outputFormat) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return color.r8g8b8;
+
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ swap(color.r8, color.b8);
+ return color.r8g8b8;
+
+ case HAL_PIXEL_FORMAT_RGB_565: {
+ // NB: we could do a higher-quality downsample here, but we
+ // want the results to be a pixel-perfect match with the fast
+ // downsample in TransformTo565().
+ uint16_t color565 = ((color.r8 & 0xF8) << 8) |
+ ((color.g8 & 0xFC) << 3) |
+ ((color.b8 ) >> 3);
+ return (color565 << 16) | color565;
+ }
+ default:
+ LOGW("Unhandled pixel format %d; falling back on black", outputFormat);
+ return 0;
+ }
+}
+
+void
+ShowSolidColorFrame(GonkDisplay *aDisplay,
+ const gralloc_module_t *grallocModule,
+ int32_t aFormat)
+{
+ LOGW("Show solid color frame for bootAnim");
+
+ ANativeWindowBuffer *buffer = aDisplay->DequeueBuffer();
+ void *mappedAddress = nullptr;
+
+ if (!buffer) {
+ LOGW("Failed to get an ANativeWindowBuffer");
+ return;
+ }
+
+ if (!grallocModule->lock(grallocModule, buffer->handle,
+ GRALLOC_USAGE_SW_READ_NEVER |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_FB,
+ 0, 0, buffer->width, buffer->height, &mappedAddress)) {
+ // Just show a black solid color frame.
+ memset(mappedAddress, 0, buffer->height * buffer->stride * GetFormatBPP(aFormat));
+ grallocModule->unlock(grallocModule, buffer->handle);
+ }
+
+ aDisplay->QueueBuffer(buffer);
+}
+
+static void *
+AnimationThread(void *)
+{
+ GonkDisplay *display = GetGonkDisplay();
+ int32_t format = display->surfaceformat;
+
+ const hw_module_t *module = nullptr;
+ if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) {
+ LOGW("Could not get gralloc module");
+ return nullptr;
+ }
+ const gralloc_module_t *grmodule =
+ reinterpret_cast<gralloc_module_t const*>(module);
+
+ ZipReader reader;
+ if (!reader.OpenArchive("/system/media/bootanimation.zip")) {
+ LOGW("Could not open boot animation");
+ ShowSolidColorFrame(display, grmodule, format);
+ return nullptr;
+ }
+
+ const cdir_entry *entry = nullptr;
+ const local_file_header *file = nullptr;
+ while ((entry = reader.GetNextEntry(entry))) {
+ string name = reader.GetEntryName(entry);
+ if (!name.compare("desc.txt")) {
+ file = reader.GetLocalEntry(entry);
+ break;
+ }
+ }
+
+ if (!file) {
+ LOGW("Could not find desc.txt in boot animation");
+ ShowSolidColorFrame(display, grmodule, format);
+ return nullptr;
+ }
+
+ string descCopy;
+ descCopy.append(file->GetData(), entry->GetDataSize());
+ int32_t width, height, fps;
+ const char *line = descCopy.c_str();
+ const char *end;
+ bool headerRead = true;
+ vector<AnimationPart> parts;
+ bool animPlayed = false;
+
+ /*
+ * bootanimation.zip
+ *
+ * This is the boot animation file format that Android uses.
+ * It's a zip file with a directories containing png frames
+ * and a desc.txt that describes how they should be played.
+ *
+ * desc.txt contains two types of lines
+ * 1. [width] [height] [fps]
+ * There is one of these lines per bootanimation.
+ * If the width and height are smaller than the screen,
+ * the frames are centered on a black background.
+ * XXX: Currently we stretch instead of centering the frame.
+ * 2. p [count] [pause] [path]
+ * This describes one animation part.
+ * Each animation part is played in sequence.
+ * An animation part contains all the files/frames in the
+ * directory specified in [path]
+ * [count] indicates the number of times this part repeats.
+ * [pause] indicates the number of frames that this part
+ * should pause for after playing the full sequence but
+ * before repeating.
+ */
+
+ do {
+ end = strstr(line, "\n");
+
+ AnimationPart part;
+ if (headerRead &&
+ sscanf(line, "%d %d %d", &width, &height, &fps) == 3) {
+ headerRead = false;
+ } else if (part.ReadFromString(line)) {
+ parts.push_back(part);
+ }
+ } while (end && *(line = end + 1));
+
+ for (uint32_t i = 0; i < parts.size(); i++) {
+ AnimationPart &part = parts[i];
+ entry = nullptr;
+ char search[256];
+ snprintf(search, sizeof(search), "%s/", part.path);
+ while ((entry = reader.GetNextEntry(entry))) {
+ string name = reader.GetEntryName(entry);
+ if (name.find(search) ||
+ !entry->GetDataSize() ||
+ name.length() >= 256)
+ continue;
+
+ part.frames.resize(part.frames.size() + 1);
+ AnimationFrame &frame = part.frames.back();
+ strcpy(frame.path, name.c_str());
+ frame.file = reader.GetLocalEntry(entry);
+ }
+
+ sort(part.frames.begin(), part.frames.end());
+ }
+
+ long int frameDelayUs = 1000000 / fps;
+
+ for (uint32_t i = 0; i < parts.size(); i++) {
+ AnimationPart &part = parts[i];
+
+ int32_t j = 0;
+ while (sRunAnimation && (!part.count || j++ < part.count)) {
+ for (uint32_t k = 0; k < part.frames.size(); k++) {
+ struct timeval tv1, tv2;
+ gettimeofday(&tv1, nullptr);
+ AnimationFrame &frame = part.frames[k];
+ if (!frame.buf) {
+ frame.ReadPngFrame(format);
+ }
+
+ ANativeWindowBuffer *buf = display->DequeueBuffer();
+ if (!buf) {
+ LOGW("Failed to get an ANativeWindowBuffer");
+ 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");
+ display->QueueBuffer(buf);
+ break;
+ }
+
+ if (frame.has_bgcolor) {
+ wchar_t bgfill = AsBackgroundFill(frame.bgcolor, format);
+ wmemset((wchar_t*)vaddr, bgfill,
+ (buf->height * buf->stride * frame.bytepp) / sizeof(wchar_t));
+ }
+
+ if ((uint32_t)buf->height == frame.height && (uint32_t)buf->stride == frame.width) {
+ memcpy(vaddr, frame.buf,
+ frame.width * frame.height * frame.bytepp);
+ } else if ((uint32_t)buf->height >= frame.height &&
+ (uint32_t)buf->width >= frame.width) {
+ int startx = (buf->width - frame.width) / 2;
+ int starty = (buf->height - frame.height) / 2;
+
+ int src_stride = frame.width * frame.bytepp;
+ int dst_stride = buf->stride * frame.bytepp;
+
+ char *src = frame.buf;
+ char *dst = (char *) vaddr + starty * dst_stride + startx * frame.bytepp;
+
+ for (uint32_t i = 0; i < frame.height; i++) {
+ memcpy(dst, src, src_stride);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ }
+ grmodule->unlock(grmodule, buf->handle);
+
+ gettimeofday(&tv2, nullptr);
+
+ timersub(&tv2, &tv1, &tv2);
+
+ if (tv2.tv_usec < frameDelayUs) {
+ usleep(frameDelayUs - tv2.tv_usec);
+ } else {
+ LOGW("Frame delay is %ld us but decoding took %ld us",
+ frameDelayUs, tv2.tv_usec);
+ }
+
+ animPlayed = true;
+ display->QueueBuffer(buf);
+
+ if (part.count && j >= part.count) {
+ free(frame.buf);
+ frame.buf = nullptr;
+ }
+ }
+ usleep(frameDelayUs * part.pause);
+ }
+ }
+
+ if (!animPlayed) {
+ ShowSolidColorFrame(display, grmodule, format);
+ }
+
+ return nullptr;
+}
+
+namespace mozilla {
+
+__attribute__ ((visibility ("default")))
+void
+StartBootAnimation()
+{
+ GetGonkDisplay(); // Ensure GonkDisplay exist
+ sRunAnimation = true;
+ pthread_create(&sAnimationThread, nullptr, AnimationThread, nullptr);
+}
+
+__attribute__ ((visibility ("default")))
+void
+StopBootAnimation()
+{
+ if (sRunAnimation) {
+ sRunAnimation = false;
+ pthread_join(sAnimationThread, nullptr);
+ GetGonkDisplay()->NotifyBootAnimationStopped();
+ }
+}
+
+} // namespace mozilla
diff --git a/widget/gonk/libdisplay/BootAnimation.h b/widget/gonk/libdisplay/BootAnimation.h
new file mode 100644
index 000000000..9fdc20eca
--- /dev/null
+++ b/widget/gonk/libdisplay/BootAnimation.h
@@ -0,0 +1,30 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BOOTANIMATION_H
+#define BOOTANIMATION_H
+
+namespace mozilla {
+
+MOZ_EXPORT __attribute__ ((weak))
+void StartBootAnimation();
+
+/* Stop the boot animation if it's still running. */
+MOZ_EXPORT __attribute__ ((weak))
+void StopBootAnimation();
+
+} // namespace mozilla
+
+#endif /* BOOTANIMATION_H */
diff --git a/widget/gonk/libdisplay/DisplaySurface.h b/widget/gonk/libdisplay/DisplaySurface.h
new file mode 100644
index 000000000..398541c49
--- /dev/null
+++ b/widget/gonk/libdisplay/DisplaySurface.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_DISPLAY_SURFACE_H
+#define ANDROID_SF_DISPLAY_SURFACE_H
+
+#include <gui/ConsumerBase.h>
+#include <system/window.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class IGraphicBufferProducer;
+class String8;
+
+#if ANDROID_VERSION >= 21
+typedef IGraphicBufferConsumer StreamConsumer;
+#else
+typedef BufferQueue StreamConsumer;
+#endif
+
+class DisplaySurface : public ConsumerBase {
+public:
+ // beginFrame is called at the beginning of the composition loop, before
+ // the configuration is known. The DisplaySurface should do anything it
+ // needs to do to enable HWComposer to decide how to compose the frame.
+ // We pass in mustRecompose so we can keep VirtualDisplaySurface's state
+ // machine happy without actually queueing a buffer if nothing has changed.
+ virtual status_t beginFrame(bool mustRecompose) = 0;
+
+ // prepareFrame is called after the composition configuration is known but
+ // before composition takes place. The DisplaySurface can use the
+ // composition type to decide how to manage the flow of buffers between
+ // GLES and HWC for this frame.
+ enum CompositionType {
+ COMPOSITION_UNKNOWN = 0,
+ COMPOSITION_GLES = 1,
+ COMPOSITION_HWC = 2,
+ COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
+ };
+ virtual status_t prepareFrame(CompositionType compositionType) = 0;
+
+ // Should be called when composition rendering is complete for a frame (but
+ // eglSwapBuffers hasn't necessarily been called). Required by certain
+ // older drivers for synchronization.
+ // TODO: Remove this when we drop support for HWC 1.0.
+ virtual status_t compositionComplete() = 0;
+
+ // Inform the surface that GLES composition is complete for this frame, and
+ // the surface should make sure that HWComposer has the correct buffer for
+ // this frame. Some implementations may only push a new buffer to
+ // HWComposer if GLES composition took place, others need to push a new
+ // buffer on every frame.
+ //
+ // advanceFrame must be followed by a call to onFrameCommitted before
+ // advanceFrame may be called again.
+ virtual status_t advanceFrame() = 0;
+
+ // onFrameCommitted is called after the frame has been committed to the
+ // hardware composer. The surface collects the release fence for this
+ // frame's buffer.
+ virtual void onFrameCommitted() = 0;
+
+ virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0;
+
+ // setReleaseFenceFd stores a fence file descriptor that will signal when the
+ // current buffer is no longer being read. This fence will be returned to
+ // the producer when the current buffer is released by updateTexImage().
+ // Multiple fences can be set for a given buffer; they will be merged into
+ // a single union fence. The SurfaceTexture will close the file descriptor
+ // when finished with it.
+ virtual status_t setReleaseFenceFd(int fenceFd) = 0;
+
+ virtual int GetPrevDispAcquireFd() = 0;
+
+ buffer_handle_t lastHandle;
+
+protected:
+ DisplaySurface(const sp<StreamConsumer>& sc)
+#if ANDROID_VERSION >= 19
+ : ConsumerBase(sc, true)
+#else
+ : ConsumerBase(sc)
+#endif
+ , lastHandle(0)
+ { }
+ virtual ~DisplaySurface() {}
+};
+
+// ---------------------------------------------------------------------------
+} // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SF_DISPLAY_SURFACE_H
+
diff --git a/widget/gonk/libdisplay/FramebufferSurface.cpp b/widget/gonk/libdisplay/FramebufferSurface.cpp
new file mode 100644
index 000000000..a289acbb8
--- /dev/null
+++ b/widget/gonk/libdisplay/FramebufferSurface.cpp
@@ -0,0 +1,207 @@
+/*
+ **
+ ** Copyright 2012 The Android Open Source Project
+ **
+ ** Licensed under the Apache License Version 2.0(the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing software
+ ** distributed under the License is distributed on an "AS IS" BASIS
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <cutils/log.h>
+
+#include <utils/String8.h>
+
+#include <ui/Rect.h>
+
+#include <EGL/egl.h>
+
+#include <hardware/hardware.h>
+#if ANDROID_VERSION == 17
+#include <gui/SurfaceTextureClient.h>
+#endif
+#include <ui/GraphicBuffer.h>
+
+#include "FramebufferSurface.h"
+#include "GraphicBufferAlloc.h"
+
+#ifndef NUM_FRAMEBUFFER_SURFACE_BUFFERS
+#define NUM_FRAMEBUFFER_SURFACE_BUFFERS (2)
+#endif
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+/*
+ * This implements the (main) framebuffer management. This class
+ * was adapted from the version in SurfaceFlinger
+ */
+FramebufferSurface::FramebufferSurface(int disp,
+ uint32_t width,
+ uint32_t height,
+ uint32_t format,
+ const sp<StreamConsumer>& sc)
+ : DisplaySurface(sc)
+ , mDisplayType(disp)
+ , mCurrentBufferSlot(-1)
+ , mCurrentBuffer(0)
+{
+ mName = "FramebufferSurface";
+
+#if ANDROID_VERSION >= 19
+ sp<IGraphicBufferConsumer> consumer = mConsumer;
+#else
+ sp<BufferQueue> consumer = mBufferQueue;
+ consumer->setSynchronousMode(true);
+#endif
+ consumer->setConsumerName(mName);
+ consumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_COMPOSER);
+ consumer->setDefaultBufferFormat(format);
+ consumer->setDefaultBufferSize(width, height);
+ consumer->setDefaultMaxBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS);
+}
+
+status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
+ return NO_ERROR;
+}
+
+status_t FramebufferSurface::prepareFrame(CompositionType /*compositionType*/) {
+ return NO_ERROR;
+}
+
+status_t FramebufferSurface::advanceFrame() {
+ // Once we remove FB HAL support, we can call nextBuffer() from here
+ // instead of using onFrameAvailable(). No real benefit, except it'll be
+ // more like VirtualDisplaySurface.
+ return NO_ERROR;
+}
+
+status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
+ Mutex::Autolock lock(mMutex);
+
+ BufferQueue::BufferItem item;
+#if ANDROID_VERSION >= 19
+ status_t err = acquireBufferLocked(&item, 0);
+#else
+ status_t err = acquireBufferLocked(&item);
+#endif
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ outBuffer = mCurrentBuffer;
+ return NO_ERROR;
+ } else if (err != NO_ERROR) {
+ ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ // If the BufferQueue has freed and reallocated a buffer in mCurrentSlot
+ // then we may have acquired the slot we already own. If we had released
+ // our current buffer before we call acquireBuffer then that release call
+ // would have returned STALE_BUFFER_SLOT, and we would have called
+ // freeBufferLocked on that slot. Because the buffer slot has already
+ // been overwritten with the new buffer all we have to do is skip the
+ // releaseBuffer call and we should be in the same state we'd be in if we
+ // had released the old buffer first.
+ if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &&
+ item.mBuf != mCurrentBufferSlot) {
+ // Release the previous buffer.
+#if ANDROID_VERSION >= 19
+ err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+#else
+ err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+#endif
+ if (err != NO_ERROR && err != StreamConsumer::STALE_BUFFER_SLOT) {
+ ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
+ return err;
+ }
+ }
+ mCurrentBufferSlot = item.mBuf;
+ mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
+ outFence = item.mFence;
+ outBuffer = mCurrentBuffer;
+ return NO_ERROR;
+}
+
+// Overrides ConsumerBase::onFrameAvailable(), does not call base class impl.
+#if ANDROID_VERSION >= 22
+void FramebufferSurface::onFrameAvailable(const ::android::BufferItem &item) {
+#else
+void FramebufferSurface::onFrameAvailable() {
+#endif
+ sp<GraphicBuffer> buf;
+ sp<Fence> acquireFence;
+ status_t err = nextBuffer(buf, acquireFence);
+ if (err != NO_ERROR) {
+ ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
+ strerror(-err), err);
+ return;
+ }
+ if (acquireFence.get() && acquireFence->isValid())
+ mPrevFBAcquireFence = new Fence(acquireFence->dup());
+ else
+ mPrevFBAcquireFence = Fence::NO_FENCE;
+
+ lastHandle = buf->handle;
+}
+
+void FramebufferSurface::freeBufferLocked(int slotIndex) {
+ ConsumerBase::freeBufferLocked(slotIndex);
+ if (slotIndex == mCurrentBufferSlot) {
+ mCurrentBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
+ }
+}
+
+status_t FramebufferSurface::setReleaseFenceFd(int fenceFd) {
+ status_t err = NO_ERROR;
+ if (fenceFd >= 0) {
+ sp<Fence> fence(new Fence(fenceFd));
+ if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) {
+#if ANDROID_VERSION >= 19
+ status_t err = addReleaseFence(mCurrentBufferSlot, mCurrentBuffer, fence);
+#else
+ status_t err = addReleaseFence(mCurrentBufferSlot, fence);
+#endif
+ ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+ return err;
+}
+
+int FramebufferSurface::GetPrevDispAcquireFd() {
+ if (mPrevFBAcquireFence.get() && mPrevFBAcquireFence->isValid()) {
+ return mPrevFBAcquireFence->dup();
+ }
+ return -1;
+}
+
+void FramebufferSurface::onFrameCommitted() {
+ // XXX This role is almost same to setReleaseFenceFd().
+}
+
+status_t FramebufferSurface::compositionComplete()
+{
+ // Actual implementaiton is in GonkDisplay::SwapBuffers()
+ // XXX need to move that to here.
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/widget/gonk/libdisplay/FramebufferSurface.h b/widget/gonk/libdisplay/FramebufferSurface.h
new file mode 100644
index 000000000..c1cc84272
--- /dev/null
+++ b/widget/gonk/libdisplay/FramebufferSurface.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
+#define ANDROID_SF_FRAMEBUFFER_SURFACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "DisplaySurface.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class Rect;
+class String8;
+
+// ---------------------------------------------------------------------------
+
+class FramebufferSurface : public DisplaySurface {
+public:
+ FramebufferSurface(int disp, uint32_t width, uint32_t height, uint32_t format, const sp<StreamConsumer>& sc);
+
+ // From DisplaySurface
+ virtual status_t beginFrame(bool mustRecompose);
+ virtual status_t prepareFrame(CompositionType compositionType);
+ virtual status_t compositionComplete();
+ virtual status_t advanceFrame();
+ virtual void onFrameCommitted();
+ // Cannot resize a buffers in a FramebufferSurface. Only works with virtual
+ // displays.
+ virtual void resizeBuffers(const uint32_t /*w*/, const uint32_t /*h*/) { };
+
+ // setReleaseFenceFd stores a fence file descriptor that will signal when the
+ // current buffer is no longer being read. This fence will be returned to
+ // the producer when the current buffer is released by updateTexImage().
+ // Multiple fences can be set for a given buffer; they will be merged into
+ // a single union fence. The SurfaceTexture will close the file descriptor
+ // when finished with it.
+ status_t setReleaseFenceFd(int fenceFd);
+
+ virtual int GetPrevDispAcquireFd();
+
+private:
+ virtual ~FramebufferSurface() { }; // this class cannot be overloaded
+
+#if ANDROID_VERSION >= 22
+ virtual void onFrameAvailable(const ::android::BufferItem &item);
+#else
+ virtual void onFrameAvailable();
+#endif
+ virtual void freeBufferLocked(int slotIndex);
+
+ // nextBuffer waits for and then latches the next buffer from the
+ // BufferQueue and releases the previously latched buffer to the
+ // BufferQueue. The new buffer is returned in the 'buffer' argument.
+ status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence);
+
+ // mDisplayType must match one of the HWC display types
+ int mDisplayType;
+
+ // mCurrentBufferIndex is the slot index of the current buffer or
+ // INVALID_BUFFER_SLOT to indicate that either there is no current buffer
+ // or the buffer is not associated with a slot.
+ int mCurrentBufferSlot;
+
+ // mCurrentBuffer is the current buffer or NULL to indicate that there is
+ // no current buffer.
+ sp<GraphicBuffer> mCurrentBuffer;
+
+ android::sp<android::Fence> mPrevFBAcquireFence;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SF_FRAMEBUFFER_SURFACE_H
+
diff --git a/widget/gonk/libdisplay/GonkDisplay.h b/widget/gonk/libdisplay/GonkDisplay.h
new file mode 100644
index 000000000..96978a6e9
--- /dev/null
+++ b/widget/gonk/libdisplay/GonkDisplay.h
@@ -0,0 +1,91 @@
+/* Copyright 2013 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GONKDISPLAY_H
+#define GONKDISPLAY_H
+
+#include <system/window.h>
+#include <utils/StrongPointer.h>
+#include "mozilla/Types.h"
+
+namespace android {
+class DisplaySurface;
+class IGraphicBufferProducer;
+}
+
+namespace mozilla {
+
+typedef void * EGLDisplay;
+typedef void * EGLSurface;
+
+class MOZ_EXPORT GonkDisplay {
+public:
+ /**
+ * This enum is for types of display. DISPLAY_PRIMARY refers to the default
+ * built-in display, DISPLAY_EXTERNAL refers to displays connected with
+ * HDMI, and DISPLAY_VIRTUAL are displays which makes composited output
+ * available within the system. Currently, displays of external are detected
+ * via the hotplug detection in HWC, and displays of virtual are connected
+ * via Wifi Display.
+ */
+ enum DisplayType {
+ DISPLAY_PRIMARY,
+ DISPLAY_EXTERNAL,
+ DISPLAY_VIRTUAL,
+ NUM_DISPLAY_TYPES
+ };
+
+ struct NativeData {
+ android::sp<ANativeWindow> mNativeWindow;
+#if ANDROID_VERSION >= 17
+ android::sp<android::DisplaySurface> mDisplaySurface;
+#endif
+ float mXdpi;
+ };
+
+ virtual void SetEnabled(bool enabled) = 0;
+
+ typedef void (*OnEnabledCallbackType)(bool enabled);
+
+ virtual void OnEnabled(OnEnabledCallbackType callback) = 0;
+
+ virtual void* GetHWCDevice() = 0;
+
+ /**
+ * Only GonkDisplayICS uses arguments.
+ */
+ virtual bool SwapBuffers(EGLDisplay dpy, EGLSurface sur) = 0;
+
+ virtual ANativeWindowBuffer* DequeueBuffer() = 0;
+
+ virtual bool QueueBuffer(ANativeWindowBuffer* buf) = 0;
+
+ virtual void UpdateDispSurface(EGLDisplay dpy, EGLSurface sur) = 0;
+
+ virtual NativeData GetNativeData(
+ GonkDisplay::DisplayType aDisplayType,
+ android::IGraphicBufferProducer* aSink = nullptr) = 0;
+
+ virtual void NotifyBootAnimationStopped() = 0;
+
+ float xdpi;
+ int32_t surfaceformat;
+};
+
+MOZ_EXPORT __attribute__ ((weak))
+GonkDisplay* GetGonkDisplay();
+
+}
+#endif /* GONKDISPLAY_H */
diff --git a/widget/gonk/libdisplay/GonkDisplayJB.cpp b/widget/gonk/libdisplay/GonkDisplayJB.cpp
new file mode 100644
index 000000000..197b85a47
--- /dev/null
+++ b/widget/gonk/libdisplay/GonkDisplayJB.cpp
@@ -0,0 +1,461 @@
+/* Copyright 2013 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GonkDisplayJB.h"
+#if ANDROID_VERSION == 17
+#include <gui/SurfaceTextureClient.h>
+#else
+#include <gui/Surface.h>
+#include <gui/GraphicBufferAlloc.h>
+#endif
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/power.h>
+#include <suspend/autosuspend.h>
+
+#if ANDROID_VERSION >= 19
+#include "VirtualDisplaySurface.h"
+#endif
+#include "FramebufferSurface.h"
+#if ANDROID_VERSION == 17
+#include "GraphicBufferAlloc.h"
+#endif
+#include "mozilla/Assertions.h"
+
+#define DEFAULT_XDPI 75.0
+
+using namespace android;
+
+namespace mozilla {
+
+static GonkDisplayJB* sGonkDisplay = nullptr;
+
+GonkDisplayJB::GonkDisplayJB()
+ : mModule(nullptr)
+ , mFBModule(nullptr)
+ , mHwc(nullptr)
+ , mFBDevice(nullptr)
+ , mPowerModule(nullptr)
+ , mList(nullptr)
+ , mWidth(0)
+ , mHeight(0)
+ , mEnabledCallback(nullptr)
+{
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &mFBModule);
+ ALOGW_IF(err, "%s module not found", GRALLOC_HARDWARE_MODULE_ID);
+ if (!err) {
+ err = framebuffer_open(mFBModule, &mFBDevice);
+ ALOGW_IF(err, "could not open framebuffer");
+ }
+
+ if (!err && mFBDevice) {
+ mWidth = mFBDevice->width;
+ mHeight = mFBDevice->height;
+ xdpi = mFBDevice->xdpi;
+ /* The emulator actually reports RGBA_8888, but EGL doesn't return
+ * any matching configuration. We force RGBX here to fix it. */
+ surfaceformat = HAL_PIXEL_FORMAT_RGBX_8888;
+ }
+
+ err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
+ ALOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
+ if (!err) {
+ err = hwc_open_1(mModule, &mHwc);
+ ALOGE_IF(err, "%s device failed to initialize (%s)",
+ HWC_HARDWARE_COMPOSER, strerror(-err));
+ }
+
+ /* Fallback on the FB rendering path instead of trying to support HWC 1.0 */
+ if (!err && mHwc->common.version == HWC_DEVICE_API_VERSION_1_0) {
+ hwc_close_1(mHwc);
+ mHwc = nullptr;
+ }
+
+ if (!err && mHwc) {
+ if (mFBDevice) {
+ framebuffer_close(mFBDevice);
+ mFBDevice = nullptr;
+ }
+
+ int32_t values[3];
+ const uint32_t attrs[] = {
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_NO_ATTRIBUTE
+ };
+ mHwc->getDisplayAttributes(mHwc, 0, 0, attrs, values);
+
+ mWidth = values[0];
+ mHeight = values[1];
+ xdpi = values[2] / 1000.0f;
+ surfaceformat = HAL_PIXEL_FORMAT_RGBA_8888;
+ }
+
+ err = hw_get_module(POWER_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&mPowerModule);
+ if (!err)
+ mPowerModule->init(mPowerModule);
+ ALOGW_IF(err, "Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err));
+
+ mAlloc = new GraphicBufferAlloc();
+
+ CreateFramebufferSurface(mSTClient, mDispSurface, mWidth, mHeight);
+
+ mList = (hwc_display_contents_1_t *)calloc(1, sizeof(*mList) + (sizeof(hwc_layer_1_t)*2));
+
+ uint32_t usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
+ if (mFBDevice) {
+ // If device uses fb, they can not use single buffer for boot animation
+ mSTClient->perform(mSTClient.get(), NATIVE_WINDOW_SET_BUFFER_COUNT, 2);
+ mSTClient->perform(mSTClient.get(), NATIVE_WINDOW_SET_USAGE, usage);
+ } else if (mHwc) {
+ PowerOnDisplay(HWC_DISPLAY_PRIMARY);
+ // For devices w/ hwc v1.0 or no hwc, this buffer can not be created,
+ // only create this buffer for devices w/ hwc version > 1.0.
+ CreateFramebufferSurface(mBootAnimSTClient, mBootAnimDispSurface, mWidth, mHeight);
+ }
+}
+
+GonkDisplayJB::~GonkDisplayJB()
+{
+ if (mHwc)
+ hwc_close_1(mHwc);
+ if (mFBDevice)
+ framebuffer_close(mFBDevice);
+ free(mList);
+}
+
+void
+GonkDisplayJB::CreateFramebufferSurface(android::sp<ANativeWindow>& aNativeWindow,
+ android::sp<android::DisplaySurface>& aDisplaySurface,
+ uint32_t aWidth,
+ uint32_t aHeight)
+{
+#if ANDROID_VERSION >= 21
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer, mAlloc);
+#elif ANDROID_VERSION >= 19
+ sp<BufferQueue> consumer = new BufferQueue(mAlloc);
+ sp<IGraphicBufferProducer> producer = consumer;
+#elif ANDROID_VERSION >= 18
+ sp<BufferQueue> consumer = new BufferQueue(true, mAlloc);
+ sp<IGraphicBufferProducer> producer = consumer;
+#else
+ sp<BufferQueue> consumer = new BufferQueue(true, mAlloc);
+#endif
+
+ aDisplaySurface = new FramebufferSurface(0, aWidth, aHeight, surfaceformat, consumer);
+
+#if ANDROID_VERSION == 17
+ aNativeWindow = new SurfaceTextureClient(
+ static_cast<sp<ISurfaceTexture>>(aDisplaySurface->getBufferQueue()));
+#else
+ aNativeWindow = new Surface(producer);
+#endif
+}
+
+void
+GonkDisplayJB::CreateVirtualDisplaySurface(android::IGraphicBufferProducer* aSink,
+ android::sp<ANativeWindow>& aNativeWindow,
+ android::sp<android::DisplaySurface>& aDisplaySurface)
+{
+#if ANDROID_VERSION >= 21
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer, mAlloc);
+#elif ANDROID_VERSION >= 19
+ sp<BufferQueue> consumer = new BufferQueue(mAlloc);
+ sp<IGraphicBufferProducer> producer = consumer;
+#endif
+
+#if ANDROID_VERSION >= 19
+ sp<VirtualDisplaySurface> virtualDisplay;
+ virtualDisplay = new VirtualDisplaySurface(-1, aSink, producer, consumer, String8("VirtualDisplaySurface"));
+ aDisplaySurface = virtualDisplay;
+ aNativeWindow = new Surface(virtualDisplay);
+#endif
+}
+
+void
+GonkDisplayJB::SetEnabled(bool enabled)
+{
+ if (enabled) {
+ autosuspend_disable();
+ mPowerModule->setInteractive(mPowerModule, true);
+ }
+
+ if (!enabled && mEnabledCallback) {
+ mEnabledCallback(enabled);
+ }
+
+#if ANDROID_VERSION >= 21
+ if (mHwc) {
+ if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_4) {
+ mHwc->setPowerMode(mHwc, HWC_DISPLAY_PRIMARY,
+ (enabled ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF));
+ } else {
+ mHwc->blank(mHwc, HWC_DISPLAY_PRIMARY, !enabled);
+ }
+ } else if (mFBDevice && mFBDevice->enableScreen) {
+ mFBDevice->enableScreen(mFBDevice, enabled);
+ }
+#else
+ if (mHwc && mHwc->blank) {
+ mHwc->blank(mHwc, HWC_DISPLAY_PRIMARY, !enabled);
+ } else if (mFBDevice && mFBDevice->enableScreen) {
+ mFBDevice->enableScreen(mFBDevice, enabled);
+ }
+#endif
+
+ if (enabled && mEnabledCallback) {
+ mEnabledCallback(enabled);
+ }
+
+ if (!enabled) {
+ autosuspend_enable();
+ mPowerModule->setInteractive(mPowerModule, false);
+ }
+}
+
+void
+GonkDisplayJB::OnEnabled(OnEnabledCallbackType callback)
+{
+ mEnabledCallback = callback;
+}
+
+void*
+GonkDisplayJB::GetHWCDevice()
+{
+ return mHwc;
+}
+
+bool
+GonkDisplayJB::SwapBuffers(EGLDisplay dpy, EGLSurface sur)
+{
+ // Should be called when composition rendering is complete for a frame.
+ // Only HWC v1.0 needs this call.
+ // HWC > v1.0 case, do not call compositionComplete().
+ // mFBDevice is present only when HWC is v1.0.
+ if (mFBDevice && mFBDevice->compositionComplete) {
+ mFBDevice->compositionComplete(mFBDevice);
+ }
+ return Post(mDispSurface->lastHandle, mDispSurface->GetPrevDispAcquireFd());
+}
+
+bool
+GonkDisplayJB::Post(buffer_handle_t buf, int fence)
+{
+ if (!mHwc) {
+ if (fence >= 0)
+ close(fence);
+ return !mFBDevice->post(mFBDevice, buf);
+ }
+
+ hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = {NULL};
+ const hwc_rect_t r = { 0, 0, static_cast<int>(mWidth), static_cast<int>(mHeight) };
+ displays[HWC_DISPLAY_PRIMARY] = mList;
+ mList->retireFenceFd = -1;
+ mList->numHwLayers = 2;
+ mList->flags = HWC_GEOMETRY_CHANGED;
+#if ANDROID_VERSION >= 18
+ mList->outbuf = nullptr;
+ mList->outbufAcquireFenceFd = -1;
+#endif
+ mList->hwLayers[0].compositionType = HWC_FRAMEBUFFER;
+ mList->hwLayers[0].hints = 0;
+ /* Skip this layer so the hwc module doesn't complain about null handles */
+ mList->hwLayers[0].flags = HWC_SKIP_LAYER;
+ mList->hwLayers[0].backgroundColor = {0};
+ mList->hwLayers[0].acquireFenceFd = -1;
+ mList->hwLayers[0].releaseFenceFd = -1;
+ /* hwc module checks displayFrame even though it shouldn't */
+ mList->hwLayers[0].displayFrame = r;
+ mList->hwLayers[1].compositionType = HWC_FRAMEBUFFER_TARGET;
+ mList->hwLayers[1].hints = 0;
+ mList->hwLayers[1].flags = 0;
+ mList->hwLayers[1].handle = buf;
+ mList->hwLayers[1].transform = 0;
+ mList->hwLayers[1].blending = HWC_BLENDING_NONE;
+#if ANDROID_VERSION >= 19
+ if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_3) {
+ mList->hwLayers[1].sourceCropf.left = 0;
+ mList->hwLayers[1].sourceCropf.top = 0;
+ mList->hwLayers[1].sourceCropf.right = mWidth;
+ mList->hwLayers[1].sourceCropf.bottom = mHeight;
+ } else {
+ mList->hwLayers[1].sourceCrop = r;
+ }
+#else
+ mList->hwLayers[1].sourceCrop = r;
+#endif
+ mList->hwLayers[1].displayFrame = r;
+ mList->hwLayers[1].visibleRegionScreen.numRects = 1;
+ mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[1].displayFrame;
+ mList->hwLayers[1].acquireFenceFd = fence;
+ mList->hwLayers[1].releaseFenceFd = -1;
+#if ANDROID_VERSION >= 18
+ mList->hwLayers[1].planeAlpha = 0xFF;
+#endif
+ mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
+ int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
+
+ if (!mBootAnimDispSurface.get()) {
+ mDispSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);
+ } else {
+ mBootAnimDispSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);
+ }
+
+ if (mList->retireFenceFd >= 0)
+ close(mList->retireFenceFd);
+ return !err;
+}
+
+ANativeWindowBuffer*
+GonkDisplayJB::DequeueBuffer()
+{
+ // Check for bootAnim or normal display flow.
+ sp<ANativeWindow> nativeWindow =
+ !mBootAnimSTClient.get() ? mSTClient : mBootAnimSTClient;
+
+ ANativeWindowBuffer *buf;
+ int fenceFd = -1;
+ nativeWindow->dequeueBuffer(nativeWindow.get(), &buf, &fenceFd);
+ sp<Fence> fence(new Fence(fenceFd));
+#if ANDROID_VERSION == 17
+ fence->waitForever(1000, "GonkDisplayJB_DequeueBuffer");
+ // 1000 is what Android uses. It is a warning timeout in ms.
+ // This timeout was removed in ANDROID_VERSION 18.
+#else
+ fence->waitForever("GonkDisplayJB_DequeueBuffer");
+#endif
+ return buf;
+}
+
+bool
+GonkDisplayJB::QueueBuffer(ANativeWindowBuffer* buf)
+{
+ bool success = false;
+ int error = DoQueueBuffer(buf);
+ // Check for bootAnim or normal display flow.
+ if (!mBootAnimSTClient.get()) {
+ success = Post(mDispSurface->lastHandle, mDispSurface->GetPrevDispAcquireFd());
+ } else {
+ success = Post(mBootAnimDispSurface->lastHandle, mBootAnimDispSurface->GetPrevDispAcquireFd());
+ }
+ return error == 0 && success;
+}
+
+int
+GonkDisplayJB::DoQueueBuffer(ANativeWindowBuffer* buf)
+{
+ int error = 0;
+ // Check for bootAnim or normal display flow.
+ if (!mBootAnimSTClient.get()) {
+ error = mSTClient->queueBuffer(mSTClient.get(), buf, -1);
+ } else {
+ error = mBootAnimSTClient->queueBuffer(mBootAnimSTClient.get(), buf, -1);
+ }
+ return error;
+}
+
+void
+GonkDisplayJB::UpdateDispSurface(EGLDisplay dpy, EGLSurface sur)
+{
+ if (sur != EGL_NO_SURFACE) {
+ eglSwapBuffers(dpy, sur);
+ } else {
+ // When BasicCompositor is used as Compositor,
+ // EGLSurface does not exit.
+ ANativeWindowBuffer* buf = DequeueBuffer();
+ DoQueueBuffer(buf);
+ }
+}
+
+void
+GonkDisplayJB::NotifyBootAnimationStopped()
+{
+ if (mBootAnimSTClient.get()) {
+ mBootAnimSTClient = nullptr;
+ mBootAnimDispSurface = nullptr;
+ }
+}
+
+void
+GonkDisplayJB::PowerOnDisplay(int aDpy)
+{
+ MOZ_ASSERT(mHwc);
+#if ANDROID_VERSION >= 21
+ if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_4) {
+ mHwc->setPowerMode(mHwc, aDpy, HWC_POWER_MODE_NORMAL);
+ } else {
+ mHwc->blank(mHwc, aDpy, 0);
+ }
+#else
+ mHwc->blank(mHwc, aDpy, 0);
+#endif
+}
+
+GonkDisplay::NativeData
+GonkDisplayJB::GetNativeData(GonkDisplay::DisplayType aDisplayType,
+ android::IGraphicBufferProducer* aSink)
+{
+ NativeData data;
+
+ if (aDisplayType == DISPLAY_PRIMARY) {
+ data.mNativeWindow = mSTClient;
+ data.mDisplaySurface = mDispSurface;
+ data.mXdpi = xdpi;
+ } else if (aDisplayType == DISPLAY_EXTERNAL) {
+ int32_t values[3];
+ const uint32_t attrs[] = {
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_NO_ATTRIBUTE
+ };
+ mHwc->getDisplayAttributes(mHwc, aDisplayType, 0, attrs, values);
+ int width = values[0];
+ int height = values[1];
+ // FIXME!! values[2] returns 0 for external display, which doesn't
+ // sound right, Bug 1169176 is the follow-up bug for this issue.
+ data.mXdpi = values[2] ? values[2] / 1000.f : DEFAULT_XDPI;
+ PowerOnDisplay(HWC_DISPLAY_EXTERNAL);
+ CreateFramebufferSurface(data.mNativeWindow,
+ data.mDisplaySurface,
+ width,
+ height);
+ } else if (aDisplayType == DISPLAY_VIRTUAL) {
+ data.mXdpi = xdpi;
+ CreateVirtualDisplaySurface(aSink,
+ data.mNativeWindow,
+ data.mDisplaySurface);
+ }
+
+ return data;
+}
+
+__attribute__ ((visibility ("default")))
+GonkDisplay*
+GetGonkDisplay()
+{
+ if (!sGonkDisplay)
+ sGonkDisplay = new GonkDisplayJB();
+ return sGonkDisplay;
+}
+
+} // namespace mozilla
diff --git a/widget/gonk/libdisplay/GonkDisplayJB.h b/widget/gonk/libdisplay/GonkDisplayJB.h
new file mode 100644
index 000000000..60bcdffc4
--- /dev/null
+++ b/widget/gonk/libdisplay/GonkDisplayJB.h
@@ -0,0 +1,85 @@
+/* Copyright 2013 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GONKDISPLAYJB_H
+#define GONKDISPLAYJB_H
+
+#include "DisplaySurface.h"
+#include "GonkDisplay.h"
+#include "hardware/hwcomposer.h"
+#include "hardware/power.h"
+#include "ui/Fence.h"
+#include "utils/RefBase.h"
+
+namespace mozilla {
+
+class MOZ_EXPORT GonkDisplayJB : public GonkDisplay {
+public:
+ GonkDisplayJB();
+ ~GonkDisplayJB();
+
+ virtual void SetEnabled(bool enabled);
+
+ virtual void OnEnabled(OnEnabledCallbackType callback);
+
+ virtual void* GetHWCDevice();
+
+ virtual bool SwapBuffers(EGLDisplay dpy, EGLSurface sur);
+
+ virtual ANativeWindowBuffer* DequeueBuffer();
+
+ virtual bool QueueBuffer(ANativeWindowBuffer* buf);
+
+ virtual void UpdateDispSurface(EGLDisplay dpy, EGLSurface sur);
+
+ bool Post(buffer_handle_t buf, int fence);
+
+ virtual NativeData GetNativeData(
+ GonkDisplay::DisplayType aDisplayType,
+ android::IGraphicBufferProducer* aSink = nullptr);
+
+ virtual void NotifyBootAnimationStopped();
+
+private:
+ void CreateFramebufferSurface(android::sp<ANativeWindow>& aNativeWindow,
+ android::sp<android::DisplaySurface>& aDisplaySurface,
+ uint32_t aWidth, uint32_t aHeight);
+ void CreateVirtualDisplaySurface(android::IGraphicBufferProducer* aSink,
+ android::sp<ANativeWindow>& aNativeWindow,
+ android::sp<android::DisplaySurface>& aDisplaySurface);
+
+ void PowerOnDisplay(int aDpy);
+
+ int DoQueueBuffer(ANativeWindowBuffer* buf);
+
+ hw_module_t const* mModule;
+ hw_module_t const* mFBModule;
+ hwc_composer_device_1_t* mHwc;
+ framebuffer_device_t* mFBDevice;
+ power_module_t* mPowerModule;
+ android::sp<android::DisplaySurface> mDispSurface;
+ android::sp<ANativeWindow> mSTClient;
+ android::sp<android::DisplaySurface> mBootAnimDispSurface;
+ android::sp<ANativeWindow> mBootAnimSTClient;
+ android::sp<android::IGraphicBufferAlloc> mAlloc;
+ hwc_display_contents_1_t* mList;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ OnEnabledCallbackType mEnabledCallback;
+};
+
+}
+
+#endif /* GONKDISPLAYJB_H */
diff --git a/widget/gonk/libdisplay/GraphicBufferAlloc.cpp b/widget/gonk/libdisplay/GraphicBufferAlloc.cpp
new file mode 100644
index 000000000..5722b7fe3
--- /dev/null
+++ b/widget/gonk/libdisplay/GraphicBufferAlloc.cpp
@@ -0,0 +1,53 @@
+/*
+ **
+ ** Copyright 2012 The Android Open Source Project
+ **
+ ** Licensed under the Apache License Version 2.0(the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing software
+ ** distributed under the License is distributed on an "AS IS" BASIS
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <cutils/log.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include "GraphicBufferAlloc.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+GraphicBufferAlloc::GraphicBufferAlloc() {
+}
+
+GraphicBufferAlloc::~GraphicBufferAlloc() {
+}
+
+sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t usage, status_t* error) {
+ sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(w, h, format, usage));
+ status_t err = graphicBuffer->initCheck();
+ *error = err;
+ if (err != 0 || graphicBuffer->handle == 0) {
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
+ ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
+ "failed (%s), handle=%p",
+ w, h, strerror(-err), graphicBuffer->handle);
+ return 0;
+ }
+ return graphicBuffer;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/widget/gonk/libdisplay/GraphicBufferAlloc.h b/widget/gonk/libdisplay/GraphicBufferAlloc.h
new file mode 100644
index 000000000..b08750c2f
--- /dev/null
+++ b/widget/gonk/libdisplay/GraphicBufferAlloc.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
+#define ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <ui/PixelFormat.h>
+#include <utils/Errors.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GraphicBuffer;
+
+class GraphicBufferAlloc : public BnGraphicBufferAlloc {
+public:
+ GraphicBufferAlloc();
+ virtual ~GraphicBufferAlloc();
+ virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t usage, status_t* error);
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
diff --git a/widget/gonk/libdisplay/VirtualDisplaySurface.cpp b/widget/gonk/libdisplay/VirtualDisplaySurface.cpp
new file mode 100644
index 000000000..746707885
--- /dev/null
+++ b/widget/gonk/libdisplay/VirtualDisplaySurface.cpp
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#include "VirtualDisplaySurface.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+#if defined(FORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS)
+static const bool sForceHwcCopy = true;
+#else
+static const bool sForceHwcCopy = false;
+#endif
+
+#define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
+ mDisplayName.string(), ##__VA_ARGS__)
+#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
+ mDisplayName.string(), ##__VA_ARGS__)
+#define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
+ mDisplayName.string(), ##__VA_ARGS__)
+
+__attribute__((unused))
+static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
+ switch (type) {
+ case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
+ case DisplaySurface::COMPOSITION_GLES: return "GLES";
+ case DisplaySurface::COMPOSITION_HWC: return "HWC";
+ case DisplaySurface::COMPOSITION_MIXED: return "MIXED";
+ default: return "<INVALID>";
+ }
+}
+
+VirtualDisplaySurface::VirtualDisplaySurface(int32_t dispId,
+ const sp<IGraphicBufferProducer>& sink,
+ const sp<IGraphicBufferProducer>& bqProducer,
+ const sp<StreamConsumer>& bqConsumer,
+ const String8& name)
+: DisplaySurface(bqConsumer),
+ mDisplayId(dispId),
+ mDisplayName(name),
+ mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
+ mProducerSlotSource(0),
+ mDbgState(DBG_STATE_IDLE),
+ mDbgLastCompositionType(COMPOSITION_UNKNOWN),
+ mMustRecompose(false)
+{
+ mSource[SOURCE_SINK] = sink;
+ mSource[SOURCE_SCRATCH] = bqProducer;
+
+ resetPerFrameState();
+
+ int sinkWidth, sinkHeight;
+ sink->query(NATIVE_WINDOW_WIDTH, &sinkWidth);
+ sink->query(NATIVE_WINDOW_HEIGHT, &sinkHeight);
+ mSinkBufferWidth = sinkWidth;
+ mSinkBufferHeight = sinkHeight;
+
+ // Pick the buffer format to request from the sink when not rendering to it
+ // with GLES. If the consumer needs CPU access, use the default format
+ // set by the consumer. Otherwise allow gralloc to decide the format based
+ // on usage bits.
+ int sinkUsage;
+ sink->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &sinkUsage);
+ if (sinkUsage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) {
+ int sinkFormat;
+ sink->query(NATIVE_WINDOW_FORMAT, &sinkFormat);
+ mDefaultOutputFormat = sinkFormat;
+ } else {
+ mDefaultOutputFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ }
+ mOutputFormat = mDefaultOutputFormat;
+
+ ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string());
+ mConsumer->setConsumerName(ConsumerBase::mName);
+ mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
+ mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
+ mConsumer->setDefaultMaxBufferCount(2);
+}
+
+VirtualDisplaySurface::~VirtualDisplaySurface() {
+}
+
+status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
+ if (mDisplayId < 0)
+ return NO_ERROR;
+
+ mMustRecompose = mustRecompose;
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
+ "Unexpected beginFrame() in %s state", dbgStateStr());
+ mDbgState = DBG_STATE_BEGUN;
+
+ return refreshOutputBuffer();
+}
+
+status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
+ if (mDisplayId < 0)
+ return NO_ERROR;
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
+ "Unexpected prepareFrame() in %s state", dbgStateStr());
+ mDbgState = DBG_STATE_PREPARED;
+
+ mCompositionType = compositionType;
+ if (sForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
+ // Some hardware can do RGB->YUV conversion more efficiently in hardware
+ // controlled by HWC than in hardware controlled by the video encoder.
+ // Forcing GLES-composed frames to go through an extra copy by the HWC
+ // allows the format conversion to happen there, rather than passing RGB
+ // directly to the consumer.
+ //
+ // On the other hand, when the consumer prefers RGB or can consume RGB
+ // inexpensively, this forces an unnecessary copy.
+ mCompositionType = COMPOSITION_MIXED;
+ }
+
+ if (mCompositionType != mDbgLastCompositionType) {
+ VDS_LOGV("prepareFrame: composition type changed to %s",
+ dbgCompositionTypeStr(mCompositionType));
+ mDbgLastCompositionType = mCompositionType;
+ }
+
+ if (mCompositionType != COMPOSITION_GLES &&
+ (mOutputFormat != mDefaultOutputFormat ||
+ mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
+ // We must have just switched from GLES-only to MIXED or HWC
+ // composition. Stop using the format and usage requested by the GLES
+ // driver; they may be suboptimal when HWC is writing to the output
+ // buffer. For example, if the output is going to a video encoder, and
+ // HWC can write directly to YUV, some hardware can skip a
+ // memory-to-memory RGB-to-YUV conversion step.
+ //
+ // If we just switched *to* GLES-only mode, we'll change the
+ // format/usage and get a new buffer when the GLES driver calls
+ // dequeueBuffer().
+ mOutputFormat = mDefaultOutputFormat;
+ mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
+ refreshOutputBuffer();
+ }
+
+ return NO_ERROR;
+}
+
+status_t VirtualDisplaySurface::compositionComplete() {
+ return NO_ERROR;
+}
+
+status_t VirtualDisplaySurface::advanceFrame() {
+ return NO_ERROR;
+
+// XXX Add HWC support
+
+#if 0
+ if (mDisplayId < 0)
+ return NO_ERROR;
+
+ if (mCompositionType == COMPOSITION_HWC) {
+ VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
+ "Unexpected advanceFrame() in %s state on HWC frame",
+ dbgStateStr());
+ } else {
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
+ "Unexpected advanceFrame() in %s state on GLES/MIXED frame",
+ dbgStateStr());
+ }
+ mDbgState = DBG_STATE_HWC;
+
+ if (mOutputProducerSlot < 0 ||
+ (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
+ // Last chance bailout if something bad happened earlier. For example,
+ // in a GLES configuration, if the sink disappears then dequeueBuffer
+ // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+ // will soldier on. So we end up here without a buffer. There should
+ // be lots of scary messages in the log just before this.
+ VDS_LOGE("advanceFrame: no buffer, bailing out");
+ return NO_MEMORY;
+ }
+
+ sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
+ mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(NULL);
+ sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
+ VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
+ mFbProducerSlot, fbBuffer.get(),
+ mOutputProducerSlot, outBuffer.get());
+
+ // At this point we know the output buffer acquire fence,
+ // so update HWC state with it.
+ mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
+
+ status_t result = NO_ERROR;
+ if (fbBuffer != NULL) {
+ result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
+ }
+
+ return result;
+#endif
+}
+
+void VirtualDisplaySurface::onFrameCommitted() {
+ return;
+
+// XXX Add HWC support
+
+#if 0
+ if (mDisplayId < 0)
+ return;
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
+ "Unexpected onFrameCommitted() in %s state", dbgStateStr());
+ mDbgState = DBG_STATE_IDLE;
+
+ sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
+ if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
+ // release the scratch buffer back to the pool
+ Mutex::Autolock lock(mMutex);
+ int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
+ VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
+ addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence);
+ releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot],
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+ }
+
+ if (mOutputProducerSlot >= 0) {
+ int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
+ QueueBufferOutput qbo;
+ sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId);
+ VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
+ if (mMustRecompose) {
+ status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
+ QueueBufferInput(
+ systemTime(), false /* isAutoTimestamp */,
+ Rect(mSinkBufferWidth, mSinkBufferHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */,
+ true /* async*/,
+ outFence),
+ &qbo);
+ if (result == NO_ERROR) {
+ updateQueueBufferOutput(qbo);
+ }
+ } else {
+ // If the surface hadn't actually been updated, then we only went
+ // through the motions of updating the display to keep our state
+ // machine happy. We cancel the buffer to avoid triggering another
+ // re-composition and causing an infinite loop.
+ mSource[SOURCE_SINK]->cancelBuffer(sslot, outFence);
+ }
+ }
+
+ resetPerFrameState();
+#endif
+}
+
+void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
+ uint32_t tmpW, tmpH, transformHint, numPendingBuffers;
+ mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers);
+ mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers);
+
+ mSinkBufferWidth = w;
+ mSinkBufferHeight = h;
+}
+
+status_t VirtualDisplaySurface::requestBuffer(int pslot,
+ sp<GraphicBuffer>* outBuf) {
+ if (mDisplayId < 0)
+ return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
+ "Unexpected requestBuffer pslot=%d in %s state",
+ pslot, dbgStateStr());
+
+ *outBuf = mProducerBuffers[pslot];
+ return NO_ERROR;
+}
+
+status_t VirtualDisplaySurface::setBufferCount(int bufferCount) {
+ return mSource[SOURCE_SINK]->setBufferCount(bufferCount);
+}
+
+status_t VirtualDisplaySurface::dequeueBuffer(Source source,
+ uint32_t format, uint32_t usage, int* sslot, sp<Fence>* fence) {
+ LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
+ // Don't let a slow consumer block us
+ bool async = (source == SOURCE_SINK);
+
+ status_t result = mSource[source]->dequeueBuffer(sslot, fence, async,
+ mSinkBufferWidth, mSinkBufferHeight, format, usage);
+ if (result < 0)
+ return result;
+ int pslot = mapSource2ProducerSlot(source, *sslot);
+ VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d",
+ dbgSourceStr(source), *sslot, pslot, result);
+ uint64_t sourceBit = static_cast<uint64_t>(source) << pslot;
+
+ if ((mProducerSlotSource & (1ULL << pslot)) != sourceBit) {
+ // This slot was previously dequeued from the other source; must
+ // re-request the buffer.
+ result |= BUFFER_NEEDS_REALLOCATION;
+ mProducerSlotSource &= ~(1ULL << pslot);
+ mProducerSlotSource |= sourceBit;
+ }
+
+ if (result & RELEASE_ALL_BUFFERS) {
+ for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if ((mProducerSlotSource & (1ULL << i)) == sourceBit)
+ mProducerBuffers[i].clear();
+ }
+ }
+ if (result & BUFFER_NEEDS_REALLOCATION) {
+ result = mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]);
+ if (result < 0) {
+ mProducerBuffers[pslot].clear();
+ mSource[source]->cancelBuffer(*sslot, *fence);
+ return result;
+ }
+ VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#x",
+ dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
+ mProducerBuffers[pslot]->getPixelFormat(),
+ mProducerBuffers[pslot]->getUsage());
+ }
+
+ return result;
+}
+
+status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+ if (mDisplayId < 0)
+ return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, async, w, h, format, usage);
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
+ "Unexpected dequeueBuffer() in %s state", dbgStateStr());
+ mDbgState = DBG_STATE_GLES;
+
+ VDS_LOGW_IF(!async, "EGL called dequeueBuffer with !async despite eglSwapInterval(0)");
+ VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage);
+
+ status_t result = NO_ERROR;
+ Source source = fbSourceForCompositionType(mCompositionType);
+
+ if (source == SOURCE_SINK) {
+
+ if (mOutputProducerSlot < 0) {
+ // Last chance bailout if something bad happened earlier. For example,
+ // in a GLES configuration, if the sink disappears then dequeueBuffer
+ // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+ // will soldier on. So we end up here without a buffer. There should
+ // be lots of scary messages in the log just before this.
+ VDS_LOGE("dequeueBuffer: no buffer, bailing out");
+ return NO_MEMORY;
+ }
+
+ // We already dequeued the output buffer. If the GLES driver wants
+ // something incompatible, we have to cancel and get a new one. This
+ // will mean that HWC will see a different output buffer between
+ // prepare and set, but since we're in GLES-only mode already it
+ // shouldn't matter.
+
+ usage |= GRALLOC_USAGE_HW_COMPOSER;
+ const sp<GraphicBuffer>& buf = mProducerBuffers[mOutputProducerSlot];
+ if ((usage & ~buf->getUsage()) != 0 ||
+ (format != 0 && format != (uint32_t)buf->getPixelFormat()) ||
+ (w != 0 && w != mSinkBufferWidth) ||
+ (h != 0 && h != mSinkBufferHeight)) {
+ VDS_LOGV("dequeueBuffer: dequeueing new output buffer: "
+ "want %dx%d fmt=%d use=%#x, "
+ "have %dx%d fmt=%d use=%#x",
+ w, h, format, usage,
+ mSinkBufferWidth, mSinkBufferHeight,
+ buf->getPixelFormat(), buf->getUsage());
+ mOutputFormat = format;
+ mOutputUsage = usage;
+ result = refreshOutputBuffer();
+ if (result < 0)
+ return result;
+ }
+ }
+
+ if (source == SOURCE_SINK) {
+ *pslot = mOutputProducerSlot;
+ *fence = mOutputFence;
+ } else {
+ int sslot;
+ result = dequeueBuffer(source, format, usage, &sslot, fence);
+ if (result >= 0) {
+ *pslot = mapSource2ProducerSlot(source, sslot);
+ }
+ }
+ return result;
+}
+
+status_t VirtualDisplaySurface::detachBuffer(int /* slot */) {
+ VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
+status_t VirtualDisplaySurface::detachNextBuffer(
+ sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) {
+ VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
+status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
+ const sp<GraphicBuffer>& /* buffer */) {
+ VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
+status_t VirtualDisplaySurface::queueBuffer(int pslot,
+ const QueueBufferInput& input, QueueBufferOutput* output) {
+ if (mDisplayId < 0)
+ return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
+ "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
+ dbgStateStr());
+ mDbgState = DBG_STATE_GLES_DONE;
+
+ VDS_LOGV("queueBuffer pslot=%d", pslot);
+
+ status_t result;
+ if (mCompositionType == COMPOSITION_MIXED) {
+ // Queue the buffer back into the scratch pool
+ QueueBufferOutput scratchQBO;
+ int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot);
+ result = mSource[SOURCE_SCRATCH]->queueBuffer(sslot, input, &scratchQBO);
+ if (result != NO_ERROR)
+ return result;
+
+ // Now acquire the buffer from the scratch pool -- should be the same
+ // slot and fence as we just queued.
+ Mutex::Autolock lock(mMutex);
+ BufferQueue::BufferItem item;
+ result = acquireBufferLocked(&item, 0);
+ if (result != NO_ERROR)
+ return result;
+ VDS_LOGW_IF(item.mBuf != sslot,
+ "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d",
+ item.mBuf, sslot);
+ mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf);
+ mFbFence = mSlots[item.mBuf].mFence;
+
+ } else {
+ LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
+ "Unexpected queueBuffer in state %s for compositionType %s",
+ dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
+
+ // Extract the GLES release fence for HWC to acquire
+ int64_t timestamp;
+ bool isAutoTimestamp;
+ Rect crop;
+ int scalingMode;
+ uint32_t transform;
+ bool async;
+ input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode,
+ &transform, &async, &mFbFence);
+
+ mFbProducerSlot = pslot;
+ mOutputFence = mFbFence;
+ }
+
+ *output = mQueueBufferOutput;
+ return NO_ERROR;
+}
+
+void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) {
+ if (mDisplayId < 0)
+ return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
+ "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
+ dbgStateStr());
+ VDS_LOGV("cancelBuffer pslot=%d", pslot);
+ Source source = fbSourceForCompositionType(mCompositionType);
+ return mSource[source]->cancelBuffer(
+ mapProducer2SourceSlot(source, pslot), fence);
+}
+
+int VirtualDisplaySurface::query(int what, int* value) {
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ *value = mSinkBufferWidth;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ *value = mSinkBufferHeight;
+ break;
+ default:
+ return mSource[SOURCE_SINK]->query(what, value);
+ }
+ return NO_ERROR;
+}
+
+#if ANDROID_VERSION >= 21
+status_t VirtualDisplaySurface::connect(const sp<IProducerListener>& listener,
+ int api, bool producerControlledByApp,
+ QueueBufferOutput* output) {
+ QueueBufferOutput qbo;
+ status_t result = mSource[SOURCE_SINK]->connect(listener, api,
+ producerControlledByApp, &qbo);
+ if (result == NO_ERROR) {
+ updateQueueBufferOutput(qbo);
+ *output = mQueueBufferOutput;
+ }
+ return result;
+}
+#else
+status_t VirtualDisplaySurface::connect(const sp<IBinder>& token,
+ int api, bool producerControlledByApp,
+ QueueBufferOutput* output) {
+ QueueBufferOutput qbo;
+ status_t result = mSource[SOURCE_SINK]->connect(token, api, producerControlledByApp, &qbo);
+ if (result == NO_ERROR) {
+ updateQueueBufferOutput(qbo);
+ *output = mQueueBufferOutput;
+ }
+ return result;
+}
+#endif
+
+status_t VirtualDisplaySurface::disconnect(int api) {
+ return mSource[SOURCE_SINK]->disconnect(api);
+}
+
+#if ANDROID_VERSION >= 21
+status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) {
+ return INVALID_OPERATION;
+}
+#endif
+
+void VirtualDisplaySurface::allocateBuffers(bool /* async */,
+ uint32_t /* width */, uint32_t /* height */, uint32_t /* format */,
+ uint32_t /* usage */) {
+ // TODO: Should we actually allocate buffers for a virtual display?
+}
+
+void VirtualDisplaySurface::updateQueueBufferOutput(
+ const QueueBufferOutput& qbo) {
+ uint32_t w, h, transformHint, numPendingBuffers;
+ qbo.deflate(&w, &h, &transformHint, &numPendingBuffers);
+ mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers);
+}
+
+void VirtualDisplaySurface::resetPerFrameState() {
+ mCompositionType = COMPOSITION_UNKNOWN;
+ mFbFence = Fence::NO_FENCE;
+ mOutputFence = Fence::NO_FENCE;
+ mOutputProducerSlot = -1;
+ mFbProducerSlot = -1;
+}
+
+status_t VirtualDisplaySurface::refreshOutputBuffer() {
+
+ return INVALID_OPERATION;
+
+// XXX Add HWC support
+
+#if 0
+ if (mOutputProducerSlot >= 0) {
+ mSource[SOURCE_SINK]->cancelBuffer(
+ mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot),
+ mOutputFence);
+ }
+
+ int sslot;
+ status_t result = dequeueBuffer(SOURCE_SINK, mOutputFormat, mOutputUsage,
+ &sslot, &mOutputFence);
+ if (result < 0)
+ return result;
+ mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
+
+ // On GLES-only frames, we don't have the right output buffer acquire fence
+ // until after GLES calls queueBuffer(). So here we just set the buffer
+ // (for use in HWC prepare) but not the fence; we'll call this again with
+ // the proper fence once we have it.
+ result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
+ mProducerBuffers[mOutputProducerSlot]);
+
+ return result;
+#endif
+}
+
+// This slot mapping function is its own inverse, so two copies are unnecessary.
+// Both are kept to make the intent clear where the function is called, and for
+// the (unlikely) chance that we switch to a different mapping function.
+int VirtualDisplaySurface::mapSource2ProducerSlot(Source source, int sslot) {
+ if (source == SOURCE_SCRATCH) {
+ return BufferQueue::NUM_BUFFER_SLOTS - sslot - 1;
+ } else {
+ return sslot;
+ }
+}
+int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) {
+ return mapSource2ProducerSlot(source, pslot);
+}
+
+VirtualDisplaySurface::Source
+VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
+ return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
+}
+
+const char* VirtualDisplaySurface::dbgStateStr() const {
+ switch (mDbgState) {
+ case DBG_STATE_IDLE: return "IDLE";
+ case DBG_STATE_PREPARED: return "PREPARED";
+ case DBG_STATE_GLES: return "GLES";
+ case DBG_STATE_GLES_DONE: return "GLES_DONE";
+ case DBG_STATE_HWC: return "HWC";
+ default: return "INVALID";
+ }
+}
+
+const char* VirtualDisplaySurface::dbgSourceStr(Source s) {
+ switch (s) {
+ case SOURCE_SINK: return "SINK";
+ case SOURCE_SCRATCH: return "SCRATCH";
+ default: return "INVALID";
+ }
+}
+
+// ---------------------------------------------------------------------------
+} // namespace android
+// ---------------------------------------------------------------------------
diff --git a/widget/gonk/libdisplay/VirtualDisplaySurface.h b/widget/gonk/libdisplay/VirtualDisplaySurface.h
new file mode 100644
index 000000000..9125d8751
--- /dev/null
+++ b/widget/gonk/libdisplay/VirtualDisplaySurface.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
+#define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
+
+#include <gui/IGraphicBufferProducer.h>
+
+#include "DisplaySurface.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class HWComposer;
+class IProducerListener;
+
+/* This DisplaySurface implementation supports virtual displays, where GLES
+ * and/or HWC compose into a buffer that is then passed to an arbitrary
+ * consumer (the sink) running in another process.
+ *
+ * The simplest case is when the virtual display will never use the h/w
+ * composer -- either the h/w composer doesn't support writing to buffers, or
+ * there are more virtual displays than it supports simultaneously. In this
+ * case, the GLES driver works directly with the output buffer queue, and
+ * calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do
+ * nothing.
+ *
+ * If h/w composer might be used, then each frame will fall into one of three
+ * configurations: GLES-only, HWC-only, and MIXED composition. In all of these,
+ * we must provide a FB target buffer and output buffer for the HWC set() call.
+ *
+ * In GLES-only composition, the GLES driver is given a buffer from the sink to
+ * render into. When the GLES driver queues the buffer to the
+ * VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of
+ * immediately queueing it to the sink. The buffer is used as both the FB
+ * target and output buffer for HWC, though on these frames the HWC doesn't
+ * do any work for this display and doesn't write to the output buffer. After
+ * composition is complete, the buffer is queued to the sink.
+ *
+ * In HWC-only composition, the VirtualDisplaySurface dequeues a buffer from
+ * the sink and passes it to HWC as both the FB target buffer and output
+ * buffer. The HWC doesn't need to read from the FB target buffer, but does
+ * write to the output buffer. After composition is complete, the buffer is
+ * queued to the sink.
+ *
+ * On MIXED frames, things become more complicated, since some h/w composer
+ * implementations can't read from and write to the same buffer. This class has
+ * an internal BufferQueue that it uses as a scratch buffer pool. The GLES
+ * driver is given a scratch buffer to render into. When it finishes rendering,
+ * the buffer is queued and then immediately acquired by the
+ * VirtualDisplaySurface. The scratch buffer is then used as the FB target
+ * buffer for HWC, and a separate buffer is dequeued from the sink and used as
+ * the HWC output buffer. When HWC composition is complete, the scratch buffer
+ * is released and the output buffer is queued to the sink.
+ */
+class VirtualDisplaySurface : public DisplaySurface,
+ public BnGraphicBufferProducer {
+public:
+ VirtualDisplaySurface(int32_t dispId,
+ const sp<IGraphicBufferProducer>& sink,
+ const sp<IGraphicBufferProducer>& bqProducer,
+ const sp<StreamConsumer>& bqConsumer,
+ const String8& name);
+
+ //
+ // DisplaySurface interface
+ //
+ virtual status_t beginFrame(bool mustRecompose);
+ virtual status_t prepareFrame(CompositionType compositionType);
+ virtual status_t compositionComplete();
+ virtual status_t advanceFrame();
+ virtual void onFrameCommitted();
+ virtual void resizeBuffers(const uint32_t w, const uint32_t h);
+
+ virtual status_t setReleaseFenceFd(int fenceFd) { return INVALID_OPERATION; }
+ virtual int GetPrevDispAcquireFd() { return -1; };
+
+private:
+ enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1};
+
+ virtual ~VirtualDisplaySurface();
+
+ //
+ // IGraphicBufferProducer interface, used by the GLES driver.
+ //
+ virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
+ virtual status_t setBufferCount(int bufferCount);
+ virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ virtual status_t detachBuffer(int slot);
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence);
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
+ virtual status_t queueBuffer(int pslot,
+ const QueueBufferInput& input, QueueBufferOutput* output);
+ virtual void cancelBuffer(int pslot, const sp<Fence>& fence);
+ virtual int query(int what, int* value);
+#if ANDROID_VERSION >= 21
+ virtual status_t connect(const sp<IProducerListener>& listener,
+ int api, bool producerControlledByApp, QueueBufferOutput* output);
+#else
+ virtual status_t connect(const sp<IBinder>& token,
+ int api, bool producerControlledByApp, QueueBufferOutput* output);
+#endif
+ virtual status_t disconnect(int api);
+#if ANDROID_VERSION >= 21
+ virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
+#endif
+ virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t usage);
+
+ //
+ // Utility methods
+ //
+ static Source fbSourceForCompositionType(CompositionType type);
+ status_t dequeueBuffer(Source source, uint32_t format, uint32_t usage,
+ int* sslot, sp<Fence>* fence);
+ void updateQueueBufferOutput(const QueueBufferOutput& qbo);
+ void resetPerFrameState();
+ status_t refreshOutputBuffer();
+
+ // Both the sink and scratch buffer pools have their own set of slots
+ // ("source slots", or "sslot"). We have to merge these into the single
+ // set of slots used by the GLES producer ("producer slots" or "pslot") and
+ // internally in the VirtualDisplaySurface. To minimize the number of times
+ // a producer slot switches which source it comes from, we map source slot
+ // numbers to producer slot numbers differently for each source.
+ static int mapSource2ProducerSlot(Source source, int sslot);
+ static int mapProducer2SourceSlot(Source source, int pslot);
+
+ //
+ // Immutable after construction
+ //
+ const int32_t mDisplayId;
+ const String8 mDisplayName;
+ sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
+ uint32_t mDefaultOutputFormat;
+
+ //
+ // Inter-frame state
+ //
+
+ // To avoid buffer reallocations, we track the buffer usage and format
+ // we used on the previous frame and use it again on the new frame. If
+ // the composition type changes or the GLES driver starts requesting
+ // different usage/format, we'll get a new buffer.
+ uint32_t mOutputFormat;
+ uint32_t mOutputUsage;
+
+ // Since we present a single producer interface to the GLES driver, but
+ // are internally muxing between the sink and scratch producers, we have
+ // to keep track of which source last returned each producer slot from
+ // dequeueBuffer. Each bit in mProducerSlotSource corresponds to a producer
+ // slot. Both mProducerSlotSource and mProducerBuffers are indexed by a
+ // "producer slot"; see the mapSlot*() functions.
+ uint64_t mProducerSlotSource;
+ sp<GraphicBuffer> mProducerBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+
+ // The QueueBufferOutput with the latest info from the sink, and with the
+ // transform hint cleared. Since we defer queueBuffer from the GLES driver
+ // to the sink, we have to return the previous version.
+ QueueBufferOutput mQueueBufferOutput;
+
+ // Details of the current sink buffer. These become valid when a buffer is
+ // dequeued from the sink, and are used when queueing the buffer.
+ uint32_t mSinkBufferWidth, mSinkBufferHeight;
+
+ //
+ // Intra-frame state
+ //
+
+ // Composition type and GLES buffer source for the current frame.
+ // Valid after prepareFrame(), cleared in onFrameCommitted.
+ CompositionType mCompositionType;
+
+ // mFbFence is the fence HWC should wait for before reading the framebuffer
+ // target buffer.
+ sp<Fence> mFbFence;
+
+ // mOutputFence is the fence HWC should wait for before writing to the
+ // output buffer.
+ sp<Fence> mOutputFence;
+
+ // Producer slot numbers for the buffers to use for HWC framebuffer target
+ // and output.
+ int mFbProducerSlot;
+ int mOutputProducerSlot;
+
+ // Debug only -- track the sequence of events in each frame so we can make
+ // sure they happen in the order we expect. This class implicitly models
+ // a state machine; this enum/variable makes it explicit.
+ //
+ // +-----------+-------------------+-------------+
+ // | State | Event || Next State |
+ // +-----------+-------------------+-------------+
+ // | IDLE | beginFrame || BEGUN |
+ // | BEGUN | prepareFrame || PREPARED |
+ // | PREPARED | dequeueBuffer [1] || GLES |
+ // | PREPARED | advanceFrame [2] || HWC |
+ // | GLES | queueBuffer || GLES_DONE |
+ // | GLES_DONE | advanceFrame || HWC |
+ // | HWC | onFrameCommitted || IDLE |
+ // +-----------+-------------------++------------+
+ // [1] COMPOSITION_GLES and COMPOSITION_MIXED frames.
+ // [2] COMPOSITION_HWC frames.
+ //
+ enum DbgState {
+ // no buffer dequeued, don't know anything about the next frame
+ DBG_STATE_IDLE,
+ // output buffer dequeued, framebuffer source not yet known
+ DBG_STATE_BEGUN,
+ // output buffer dequeued, framebuffer source known but not provided
+ // to GLES yet.
+ DBG_STATE_PREPARED,
+ // GLES driver has a buffer dequeued
+ DBG_STATE_GLES,
+ // GLES driver has queued the buffer, we haven't sent it to HWC yet
+ DBG_STATE_GLES_DONE,
+ // HWC has the buffer for this frame
+ DBG_STATE_HWC,
+ };
+ DbgState mDbgState;
+ CompositionType mDbgLastCompositionType;
+
+ const char* dbgStateStr() const;
+ static const char* dbgSourceStr(Source s);
+
+ bool mMustRecompose;
+};
+
+// ---------------------------------------------------------------------------
+} // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
+
diff --git a/widget/gonk/libdisplay/moz.build b/widget/gonk/libdisplay/moz.build
new file mode 100644
index 000000000..917320592
--- /dev/null
+++ b/widget/gonk/libdisplay/moz.build
@@ -0,0 +1,59 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# Copyright 2013 Mozilla Foundation and Mozilla contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SOURCES += [
+ 'BootAnimation.cpp',
+]
+
+if CONFIG['ANDROID_VERSION'] >= '19':
+ SOURCES += [
+ 'FramebufferSurface.cpp',
+ 'GonkDisplayJB.cpp',
+ 'VirtualDisplaySurface.cpp',
+ ]
+elif CONFIG['ANDROID_VERSION'] == '18':
+ SOURCES += [
+ 'FramebufferSurface.cpp',
+ 'GonkDisplayJB.cpp',
+ ]
+elif CONFIG['ANDROID_VERSION'] == '17':
+ SOURCES += [
+ 'FramebufferSurface.cpp',
+ 'GonkDisplayJB.cpp',
+ 'GraphicBufferAlloc.cpp',
+ ]
+elif CONFIG['ANDROID_VERSION'] and CONFIG['COMPILE_ENVIRONMENT']:
+ error('Unsupported platform version: %s' % (CONFIG['ANDROID_VERSION']))
+
+Library('display')
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FORCE_STATIC_LIB = True
+
+DEFINES['XPCOM_GLUE'] = True
+
+DISABLE_STL_WRAPPING = True
+
+LOCAL_INCLUDES += [
+ '%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
+ 'frameworks/native/include/gui',
+ 'frameworks/native/opengl/include',
+ 'hardware/libhardware/include',
+ 'hardware/libhardware_legacy/include',
+ 'system/core/libsuspend/include',
+ ]
+]