blob: 9ffbf689511b9466054f39f0530dc9ce516df421 [file] [log] [blame]
/* Copyright (C) 2015 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "android/gpu-frame-bridge.h"
#include "qemu/main-loop.h"
#include "qemu/thread.h"
#include "qemu/event_notifier.h"
#include <glib.h>
#include <string.h>
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define D(...) printf(__VA_ARGS__), fflush(stdout)
#else
#define D(...) ((void)0)
#endif
// A small structure used to hold a GPU Frame in memory.
typedef struct {
int width;
int height;
void* pixels;
} GpuFrame;
static GpuFrame* gpu_frame_new(int w, int h, const void* pixels) {
GpuFrame* frame = g_new0(GpuFrame, 1);
frame->width = w;
frame->height = h;
frame->pixels = g_memdup(pixels, w * h * 4);
return frame;
}
static void gpu_frame_free(GpuFrame* frame) {
if (frame) {
g_free(frame->pixels);
g_free(frame);
}
}
// Maximum number of frames in the bridge. Trying to post more frames
// than that will block.
#define MAX_GPU_FRAMES 16
typedef struct {
bool init;
QemuMutex lock;
QemuCond can_write;
EventNotifier can_read;
int num_frames;
GpuFrame* frames[MAX_GPU_FRAMES];
GpuFrameBridgeCallback* callback;
void* callback_opaque;
} GpuBridge;
static GpuBridge s_bridge = {
.init = false,
.num_frames = 0,
.frames = { NULL, },
};
// This function is called from an EmuGL thread to post a new GPU frame.
static void emugl_frame_post(void* context,
int width,
int height,
int ydir,
int format,
int type,
unsigned char* pixels) {
GpuBridge *bridge = &s_bridge;
qemu_mutex_lock(&bridge->lock);
while (bridge->num_frames >= MAX_GPU_FRAMES) {
D("%s: frame array full\n", __FUNCTION__);
qemu_cond_wait(&bridge->can_write, &bridge->lock);
}
bridge->frames[bridge->num_frames] = gpu_frame_new(width, height, pixels);
bridge->num_frames += 1;
qemu_mutex_unlock(&bridge->lock);
event_notifier_set(&bridge->can_read);
}
// Called from the main loop whenever a new frame was notified from
// emugl_frame_post().
static void read_frame(EventNotifier* e) {
GpuBridge* bridge = &s_bridge;
if (event_notifier_test_and_clear(&bridge->can_read)) {
qemu_mutex_lock(&bridge->lock);
// Beware of spurious wakeups.
if (bridge->num_frames > 0) {
GpuFrame* frame = bridge->frames[0];
memmove(bridge->frames, bridge->frames + 1,
(bridge->num_frames - 1) * sizeof(bridge->frames[0]));
if (bridge->num_frames == MAX_GPU_FRAMES) {
D("%s: frame array no longer full\n", __FUNCTION__);
qemu_cond_signal(&bridge->can_write);
}
D("%s: new frame %dx%d\n", __FUNCTION__, frame->width,
frame->height);
bridge->callback(bridge->callback_opaque,
frame->width,
frame->height,
frame->pixels);
gpu_frame_free(frame);
bridge->num_frames -= 1;
if (bridge->num_frames > 0) {
event_notifier_set(&bridge->can_read);
}
} else {
D("%s: spurious wakeup\n", __FUNCTION__);
}
qemu_mutex_unlock(&bridge->lock);
}
}
void android_gpu_frame_bridge_init(GpuFrameBridgeCallback *callback,
void *callback_opaque)
{
GpuBridge *bridge = &s_bridge;
if (bridge->init) {
return;
}
bridge->init = true;
bridge->callback = callback;
bridge->callback_opaque = callback_opaque;
qemu_mutex_init(&bridge->lock);
qemu_cond_init(&bridge->can_write);
event_notifier_init(&bridge->can_read, 0);
event_notifier_set_handler(&bridge->can_read, read_frame);
// Ensure EmuGL will call
android_setPostCallback(&emugl_frame_post, bridge);
}