import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -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

View file

@ -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 */

View file

@ -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

View file

@ -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
// ----------------------------------------------------------------------------

View file

@ -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

View file

@ -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 */

View file

@ -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

View file

@ -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 */

View file

@ -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
// ----------------------------------------------------------------------------

View file

@ -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

View file

@ -0,0 +1,635 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// #define LOG_NDEBUG 0
#include "VirtualDisplaySurface.h"
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
#if defined(FORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS)
static const bool sForceHwcCopy = true;
#else
static const bool sForceHwcCopy = false;
#endif
#define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
mDisplayName.string(), ##__VA_ARGS__)
#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
mDisplayName.string(), ##__VA_ARGS__)
#define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
mDisplayName.string(), ##__VA_ARGS__)
__attribute__((unused))
static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
switch (type) {
case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
case DisplaySurface::COMPOSITION_GLES: return "GLES";
case DisplaySurface::COMPOSITION_HWC: return "HWC";
case DisplaySurface::COMPOSITION_MIXED: return "MIXED";
default: return "<INVALID>";
}
}
VirtualDisplaySurface::VirtualDisplaySurface(int32_t dispId,
const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<StreamConsumer>& bqConsumer,
const String8& name)
: DisplaySurface(bqConsumer),
mDisplayId(dispId),
mDisplayName(name),
mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
mProducerSlotSource(0),
mDbgState(DBG_STATE_IDLE),
mDbgLastCompositionType(COMPOSITION_UNKNOWN),
mMustRecompose(false)
{
mSource[SOURCE_SINK] = sink;
mSource[SOURCE_SCRATCH] = bqProducer;
resetPerFrameState();
int sinkWidth, sinkHeight;
sink->query(NATIVE_WINDOW_WIDTH, &sinkWidth);
sink->query(NATIVE_WINDOW_HEIGHT, &sinkHeight);
mSinkBufferWidth = sinkWidth;
mSinkBufferHeight = sinkHeight;
// Pick the buffer format to request from the sink when not rendering to it
// with GLES. If the consumer needs CPU access, use the default format
// set by the consumer. Otherwise allow gralloc to decide the format based
// on usage bits.
int sinkUsage;
sink->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &sinkUsage);
if (sinkUsage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) {
int sinkFormat;
sink->query(NATIVE_WINDOW_FORMAT, &sinkFormat);
mDefaultOutputFormat = sinkFormat;
} else {
mDefaultOutputFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
}
mOutputFormat = mDefaultOutputFormat;
ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string());
mConsumer->setConsumerName(ConsumerBase::mName);
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
mConsumer->setDefaultMaxBufferCount(2);
}
VirtualDisplaySurface::~VirtualDisplaySurface() {
}
status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
if (mDisplayId < 0)
return NO_ERROR;
mMustRecompose = mustRecompose;
VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
"Unexpected beginFrame() in %s state", dbgStateStr());
mDbgState = DBG_STATE_BEGUN;
return refreshOutputBuffer();
}
status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
if (mDisplayId < 0)
return NO_ERROR;
VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
"Unexpected prepareFrame() in %s state", dbgStateStr());
mDbgState = DBG_STATE_PREPARED;
mCompositionType = compositionType;
if (sForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
// Some hardware can do RGB->YUV conversion more efficiently in hardware
// controlled by HWC than in hardware controlled by the video encoder.
// Forcing GLES-composed frames to go through an extra copy by the HWC
// allows the format conversion to happen there, rather than passing RGB
// directly to the consumer.
//
// On the other hand, when the consumer prefers RGB or can consume RGB
// inexpensively, this forces an unnecessary copy.
mCompositionType = COMPOSITION_MIXED;
}
if (mCompositionType != mDbgLastCompositionType) {
VDS_LOGV("prepareFrame: composition type changed to %s",
dbgCompositionTypeStr(mCompositionType));
mDbgLastCompositionType = mCompositionType;
}
if (mCompositionType != COMPOSITION_GLES &&
(mOutputFormat != mDefaultOutputFormat ||
mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
// We must have just switched from GLES-only to MIXED or HWC
// composition. Stop using the format and usage requested by the GLES
// driver; they may be suboptimal when HWC is writing to the output
// buffer. For example, if the output is going to a video encoder, and
// HWC can write directly to YUV, some hardware can skip a
// memory-to-memory RGB-to-YUV conversion step.
//
// If we just switched *to* GLES-only mode, we'll change the
// format/usage and get a new buffer when the GLES driver calls
// dequeueBuffer().
mOutputFormat = mDefaultOutputFormat;
mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
refreshOutputBuffer();
}
return NO_ERROR;
}
status_t VirtualDisplaySurface::compositionComplete() {
return NO_ERROR;
}
status_t VirtualDisplaySurface::advanceFrame() {
return NO_ERROR;
// XXX Add HWC support
#if 0
if (mDisplayId < 0)
return NO_ERROR;
if (mCompositionType == COMPOSITION_HWC) {
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
"Unexpected advanceFrame() in %s state on HWC frame",
dbgStateStr());
} else {
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
"Unexpected advanceFrame() in %s state on GLES/MIXED frame",
dbgStateStr());
}
mDbgState = DBG_STATE_HWC;
if (mOutputProducerSlot < 0 ||
(mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
// Last chance bailout if something bad happened earlier. For example,
// in a GLES configuration, if the sink disappears then dequeueBuffer
// will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
VDS_LOGE("advanceFrame: no buffer, bailing out");
return NO_MEMORY;
}
sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(NULL);
sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
// At this point we know the output buffer acquire fence,
// so update HWC state with it.
mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
status_t result = NO_ERROR;
if (fbBuffer != NULL) {
result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
}
return result;
#endif
}
void VirtualDisplaySurface::onFrameCommitted() {
return;
// XXX Add HWC support
#if 0
if (mDisplayId < 0)
return;
VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
"Unexpected onFrameCommitted() in %s state", dbgStateStr());
mDbgState = DBG_STATE_IDLE;
sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
// release the scratch buffer back to the pool
Mutex::Autolock lock(mMutex);
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence);
releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot],
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
}
if (mOutputProducerSlot >= 0) {
int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
QueueBufferOutput qbo;
sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId);
VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
if (mMustRecompose) {
status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
QueueBufferInput(
systemTime(), false /* isAutoTimestamp */,
Rect(mSinkBufferWidth, mSinkBufferHeight),
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */,
true /* async*/,
outFence),
&qbo);
if (result == NO_ERROR) {
updateQueueBufferOutput(qbo);
}
} else {
// If the surface hadn't actually been updated, then we only went
// through the motions of updating the display to keep our state
// machine happy. We cancel the buffer to avoid triggering another
// re-composition and causing an infinite loop.
mSource[SOURCE_SINK]->cancelBuffer(sslot, outFence);
}
}
resetPerFrameState();
#endif
}
void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
uint32_t tmpW, tmpH, transformHint, numPendingBuffers;
mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers);
mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers);
mSinkBufferWidth = w;
mSinkBufferHeight = h;
}
status_t VirtualDisplaySurface::requestBuffer(int pslot,
sp<GraphicBuffer>* outBuf) {
if (mDisplayId < 0)
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
"Unexpected requestBuffer pslot=%d in %s state",
pslot, dbgStateStr());
*outBuf = mProducerBuffers[pslot];
return NO_ERROR;
}
status_t VirtualDisplaySurface::setBufferCount(int bufferCount) {
return mSource[SOURCE_SINK]->setBufferCount(bufferCount);
}
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
uint32_t format, uint32_t usage, int* sslot, sp<Fence>* fence) {
LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
// Don't let a slow consumer block us
bool async = (source == SOURCE_SINK);
status_t result = mSource[source]->dequeueBuffer(sslot, fence, async,
mSinkBufferWidth, mSinkBufferHeight, format, usage);
if (result < 0)
return result;
int pslot = mapSource2ProducerSlot(source, *sslot);
VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d",
dbgSourceStr(source), *sslot, pslot, result);
uint64_t sourceBit = static_cast<uint64_t>(source) << pslot;
if ((mProducerSlotSource & (1ULL << pslot)) != sourceBit) {
// This slot was previously dequeued from the other source; must
// re-request the buffer.
result |= BUFFER_NEEDS_REALLOCATION;
mProducerSlotSource &= ~(1ULL << pslot);
mProducerSlotSource |= sourceBit;
}
if (result & RELEASE_ALL_BUFFERS) {
for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
if ((mProducerSlotSource & (1ULL << i)) == sourceBit)
mProducerBuffers[i].clear();
}
}
if (result & BUFFER_NEEDS_REALLOCATION) {
result = mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]);
if (result < 0) {
mProducerBuffers[pslot].clear();
mSource[source]->cancelBuffer(*sslot, *fence);
return result;
}
VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#x",
dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
mProducerBuffers[pslot]->getPixelFormat(),
mProducerBuffers[pslot]->getUsage());
}
return result;
}
status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
if (mDisplayId < 0)
return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, async, w, h, format, usage);
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
"Unexpected dequeueBuffer() in %s state", dbgStateStr());
mDbgState = DBG_STATE_GLES;
VDS_LOGW_IF(!async, "EGL called dequeueBuffer with !async despite eglSwapInterval(0)");
VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage);
status_t result = NO_ERROR;
Source source = fbSourceForCompositionType(mCompositionType);
if (source == SOURCE_SINK) {
if (mOutputProducerSlot < 0) {
// Last chance bailout if something bad happened earlier. For example,
// in a GLES configuration, if the sink disappears then dequeueBuffer
// will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
VDS_LOGE("dequeueBuffer: no buffer, bailing out");
return NO_MEMORY;
}
// We already dequeued the output buffer. If the GLES driver wants
// something incompatible, we have to cancel and get a new one. This
// will mean that HWC will see a different output buffer between
// prepare and set, but since we're in GLES-only mode already it
// shouldn't matter.
usage |= GRALLOC_USAGE_HW_COMPOSER;
const sp<GraphicBuffer>& buf = mProducerBuffers[mOutputProducerSlot];
if ((usage & ~buf->getUsage()) != 0 ||
(format != 0 && format != (uint32_t)buf->getPixelFormat()) ||
(w != 0 && w != mSinkBufferWidth) ||
(h != 0 && h != mSinkBufferHeight)) {
VDS_LOGV("dequeueBuffer: dequeueing new output buffer: "
"want %dx%d fmt=%d use=%#x, "
"have %dx%d fmt=%d use=%#x",
w, h, format, usage,
mSinkBufferWidth, mSinkBufferHeight,
buf->getPixelFormat(), buf->getUsage());
mOutputFormat = format;
mOutputUsage = usage;
result = refreshOutputBuffer();
if (result < 0)
return result;
}
}
if (source == SOURCE_SINK) {
*pslot = mOutputProducerSlot;
*fence = mOutputFence;
} else {
int sslot;
result = dequeueBuffer(source, format, usage, &sslot, fence);
if (result >= 0) {
*pslot = mapSource2ProducerSlot(source, sslot);
}
}
return result;
}
status_t VirtualDisplaySurface::detachBuffer(int /* slot */) {
VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface");
return INVALID_OPERATION;
}
status_t VirtualDisplaySurface::detachNextBuffer(
sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) {
VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface");
return INVALID_OPERATION;
}
status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
const sp<GraphicBuffer>& /* buffer */) {
VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
return INVALID_OPERATION;
}
status_t VirtualDisplaySurface::queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output) {
if (mDisplayId < 0)
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
"Unexpected queueBuffer(pslot=%d) in %s state", pslot,
dbgStateStr());
mDbgState = DBG_STATE_GLES_DONE;
VDS_LOGV("queueBuffer pslot=%d", pslot);
status_t result;
if (mCompositionType == COMPOSITION_MIXED) {
// Queue the buffer back into the scratch pool
QueueBufferOutput scratchQBO;
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot);
result = mSource[SOURCE_SCRATCH]->queueBuffer(sslot, input, &scratchQBO);
if (result != NO_ERROR)
return result;
// Now acquire the buffer from the scratch pool -- should be the same
// slot and fence as we just queued.
Mutex::Autolock lock(mMutex);
BufferQueue::BufferItem item;
result = acquireBufferLocked(&item, 0);
if (result != NO_ERROR)
return result;
VDS_LOGW_IF(item.mBuf != sslot,
"queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d",
item.mBuf, sslot);
mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf);
mFbFence = mSlots[item.mBuf].mFence;
} else {
LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
"Unexpected queueBuffer in state %s for compositionType %s",
dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
// Extract the GLES release fence for HWC to acquire
int64_t timestamp;
bool isAutoTimestamp;
Rect crop;
int scalingMode;
uint32_t transform;
bool async;
input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode,
&transform, &async, &mFbFence);
mFbProducerSlot = pslot;
mOutputFence = mFbFence;
}
*output = mQueueBufferOutput;
return NO_ERROR;
}
void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) {
if (mDisplayId < 0)
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
"Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
dbgStateStr());
VDS_LOGV("cancelBuffer pslot=%d", pslot);
Source source = fbSourceForCompositionType(mCompositionType);
return mSource[source]->cancelBuffer(
mapProducer2SourceSlot(source, pslot), fence);
}
int VirtualDisplaySurface::query(int what, int* value) {
switch (what) {
case NATIVE_WINDOW_WIDTH:
*value = mSinkBufferWidth;
break;
case NATIVE_WINDOW_HEIGHT:
*value = mSinkBufferHeight;
break;
default:
return mSource[SOURCE_SINK]->query(what, value);
}
return NO_ERROR;
}
#if ANDROID_VERSION >= 21
status_t VirtualDisplaySurface::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp,
QueueBufferOutput* output) {
QueueBufferOutput qbo;
status_t result = mSource[SOURCE_SINK]->connect(listener, api,
producerControlledByApp, &qbo);
if (result == NO_ERROR) {
updateQueueBufferOutput(qbo);
*output = mQueueBufferOutput;
}
return result;
}
#else
status_t VirtualDisplaySurface::connect(const sp<IBinder>& token,
int api, bool producerControlledByApp,
QueueBufferOutput* output) {
QueueBufferOutput qbo;
status_t result = mSource[SOURCE_SINK]->connect(token, api, producerControlledByApp, &qbo);
if (result == NO_ERROR) {
updateQueueBufferOutput(qbo);
*output = mQueueBufferOutput;
}
return result;
}
#endif
status_t VirtualDisplaySurface::disconnect(int api) {
return mSource[SOURCE_SINK]->disconnect(api);
}
#if ANDROID_VERSION >= 21
status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) {
return INVALID_OPERATION;
}
#endif
void VirtualDisplaySurface::allocateBuffers(bool /* async */,
uint32_t /* width */, uint32_t /* height */, uint32_t /* format */,
uint32_t /* usage */) {
// TODO: Should we actually allocate buffers for a virtual display?
}
void VirtualDisplaySurface::updateQueueBufferOutput(
const QueueBufferOutput& qbo) {
uint32_t w, h, transformHint, numPendingBuffers;
qbo.deflate(&w, &h, &transformHint, &numPendingBuffers);
mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers);
}
void VirtualDisplaySurface::resetPerFrameState() {
mCompositionType = COMPOSITION_UNKNOWN;
mFbFence = Fence::NO_FENCE;
mOutputFence = Fence::NO_FENCE;
mOutputProducerSlot = -1;
mFbProducerSlot = -1;
}
status_t VirtualDisplaySurface::refreshOutputBuffer() {
return INVALID_OPERATION;
// XXX Add HWC support
#if 0
if (mOutputProducerSlot >= 0) {
mSource[SOURCE_SINK]->cancelBuffer(
mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot),
mOutputFence);
}
int sslot;
status_t result = dequeueBuffer(SOURCE_SINK, mOutputFormat, mOutputUsage,
&sslot, &mOutputFence);
if (result < 0)
return result;
mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
// On GLES-only frames, we don't have the right output buffer acquire fence
// until after GLES calls queueBuffer(). So here we just set the buffer
// (for use in HWC prepare) but not the fence; we'll call this again with
// the proper fence once we have it.
result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
mProducerBuffers[mOutputProducerSlot]);
return result;
#endif
}
// This slot mapping function is its own inverse, so two copies are unnecessary.
// Both are kept to make the intent clear where the function is called, and for
// the (unlikely) chance that we switch to a different mapping function.
int VirtualDisplaySurface::mapSource2ProducerSlot(Source source, int sslot) {
if (source == SOURCE_SCRATCH) {
return BufferQueue::NUM_BUFFER_SLOTS - sslot - 1;
} else {
return sslot;
}
}
int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) {
return mapSource2ProducerSlot(source, pslot);
}
VirtualDisplaySurface::Source
VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
}
const char* VirtualDisplaySurface::dbgStateStr() const {
switch (mDbgState) {
case DBG_STATE_IDLE: return "IDLE";
case DBG_STATE_PREPARED: return "PREPARED";
case DBG_STATE_GLES: return "GLES";
case DBG_STATE_GLES_DONE: return "GLES_DONE";
case DBG_STATE_HWC: return "HWC";
default: return "INVALID";
}
}
const char* VirtualDisplaySurface::dbgSourceStr(Source s) {
switch (s) {
case SOURCE_SINK: return "SINK";
case SOURCE_SCRATCH: return "SCRATCH";
default: return "INVALID";
}
}
// ---------------------------------------------------------------------------
} // namespace android
// ---------------------------------------------------------------------------

View file

@ -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

View file

@ -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',
]
]