diff options
Diffstat (limited to 'widget/gonk/libdisplay')
-rw-r--r-- | widget/gonk/libdisplay/BootAnimation.cpp | 726 | ||||
-rw-r--r-- | widget/gonk/libdisplay/BootAnimation.h | 30 | ||||
-rw-r--r-- | widget/gonk/libdisplay/DisplaySurface.h | 112 | ||||
-rw-r--r-- | widget/gonk/libdisplay/FramebufferSurface.cpp | 207 | ||||
-rw-r--r-- | widget/gonk/libdisplay/FramebufferSurface.h | 93 | ||||
-rw-r--r-- | widget/gonk/libdisplay/GonkDisplay.h | 91 | ||||
-rw-r--r-- | widget/gonk/libdisplay/GonkDisplayJB.cpp | 461 | ||||
-rw-r--r-- | widget/gonk/libdisplay/GonkDisplayJB.h | 85 | ||||
-rw-r--r-- | widget/gonk/libdisplay/GraphicBufferAlloc.cpp | 53 | ||||
-rw-r--r-- | widget/gonk/libdisplay/GraphicBufferAlloc.h | 44 | ||||
-rw-r--r-- | widget/gonk/libdisplay/VirtualDisplaySurface.cpp | 635 | ||||
-rw-r--r-- | widget/gonk/libdisplay/VirtualDisplaySurface.h | 250 | ||||
-rw-r--r-- | widget/gonk/libdisplay/moz.build | 59 |
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(×tamp, &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', + ] +] |