mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-22 15:17:29 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
726
widget/gonk/libdisplay/BootAnimation.cpp
Normal file
726
widget/gonk/libdisplay/BootAnimation.cpp
Normal 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
|
||||
30
widget/gonk/libdisplay/BootAnimation.h
Normal file
30
widget/gonk/libdisplay/BootAnimation.h
Normal 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 */
|
||||
112
widget/gonk/libdisplay/DisplaySurface.h
Normal file
112
widget/gonk/libdisplay/DisplaySurface.h
Normal 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
|
||||
|
||||
207
widget/gonk/libdisplay/FramebufferSurface.cpp
Normal file
207
widget/gonk/libdisplay/FramebufferSurface.cpp
Normal 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
|
||||
// ----------------------------------------------------------------------------
|
||||
93
widget/gonk/libdisplay/FramebufferSurface.h
Normal file
93
widget/gonk/libdisplay/FramebufferSurface.h
Normal 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
|
||||
|
||||
91
widget/gonk/libdisplay/GonkDisplay.h
Normal file
91
widget/gonk/libdisplay/GonkDisplay.h
Normal 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 */
|
||||
461
widget/gonk/libdisplay/GonkDisplayJB.cpp
Normal file
461
widget/gonk/libdisplay/GonkDisplayJB.cpp
Normal 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
|
||||
85
widget/gonk/libdisplay/GonkDisplayJB.h
Normal file
85
widget/gonk/libdisplay/GonkDisplayJB.h
Normal 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 */
|
||||
53
widget/gonk/libdisplay/GraphicBufferAlloc.cpp
Normal file
53
widget/gonk/libdisplay/GraphicBufferAlloc.cpp
Normal 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
|
||||
// ----------------------------------------------------------------------------
|
||||
44
widget/gonk/libdisplay/GraphicBufferAlloc.h
Normal file
44
widget/gonk/libdisplay/GraphicBufferAlloc.h
Normal 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
|
||||
635
widget/gonk/libdisplay/VirtualDisplaySurface.cpp
Normal file
635
widget/gonk/libdisplay/VirtualDisplaySurface.cpp
Normal 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(×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
|
||||
// ---------------------------------------------------------------------------
|
||||
250
widget/gonk/libdisplay/VirtualDisplaySurface.h
Normal file
250
widget/gonk/libdisplay/VirtualDisplaySurface.h
Normal 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
|
||||
|
||||
59
widget/gonk/libdisplay/moz.build
Normal file
59
widget/gonk/libdisplay/moz.build
Normal 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',
|
||||
]
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue