blob: cac803edf9e0d2af244219d254c1d2abc1686c04 [file] [log] [blame]
/*
* Copyright (C) 2011 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 "RenderControl.h"
#include "DispatchTables.h"
#include "FbConfig.h"
#include "FenceSync.h"
#include "FrameBuffer.h"
#include "RenderContext.h"
#include "RenderThreadInfo.h"
#include "SyncThread.h"
#include "ChecksumCalculatorThreadInfo.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include "android/utils/debug.h"
#include "android/base/StringView.h"
#include "emugl/common/feature_control.h"
#include "emugl/common/lazy_instance.h"
#include "emugl/common/sync_device.h"
#include "emugl/common/thread.h"
#include <atomic>
#include <inttypes.h>
#include <string.h>
using android::base::AutoLock;
using android::base::Lock;
#define DEBUG_GRALLOC_SYNC 0
#define DEBUG_EGL_SYNC 0
#define RENDERCONTROL_DPRINT(...) do { \
if (!VERBOSE_CHECK(gles)) { VERBOSE_ENABLE(gles); } \
VERBOSE_TID_FUNCTION_DPRINT(gles, __VA_ARGS__); \
} while(0)
#if DEBUG_GRALLOC_SYNC
#define GRSYNC_DPRINT RENDERCONTROL_DPRINT
#else
#define GRSYNC_DPRINT(...)
#endif
#if DEBUG_EGL_SYNC
#define EGLSYNC_DPRINT RENDERCONTROL_DPRINT
#else
#define EGLSYNC_DPRINT(...)
#endif
// GrallocSync is a class that helps to reflect the behavior of
// grallock_lock/gralloc_unlock on the guest.
// If we don't use this, apps that use gralloc buffers (such as webcam)
// will have out of order frames,
// as GL calls from different threads in the guest
// are allowed to arrive at the host in any ordering.
class GrallocSync {
public:
GrallocSync() {
// Having in-order webcam frames is nice, but not at the cost
// of potential deadlocks;
// we need to be careful of what situations in which
// we actually lock/unlock the gralloc color buffer.
//
// To avoid deadlock:
// we require rcColorBufferCacheFlush to be called
// whenever gralloc_lock is called on the guest,
// and we require rcUpdateWindowColorBuffer to be called
// whenever gralloc_unlock is called on the guest.
//
// Some versions of the system image optimize out
// the call to rcUpdateWindowColorBuffer in the case of zero
// width/height, but since we're using that as synchronization,
// that lack of calling can lead to a deadlock on the host
// in many situations
// (switching camera sides, exiting benchmark apps, etc)
// So, we put GrallocSync under the feature control.
mEnabled = emugl_feature_is_enabled(android::featurecontrol::GrallocSync);
// There are two potential tricky situations to handle:
// a. Multiple users of gralloc buffers that all want to
// call gralloc_lock. This is obeserved to happen on older APIs
// (<= 19).
// b. The pipe doesn't have to preserve ordering of the
// gralloc_lock and gralloc_unlock commands themselves.
//
// To handle a), notice the situation is one of one type of uses
// needing multiple locks that needs to exclude concurrent use
// by another type of user. This maps well to a read/write lock,
// where gralloc_lock and gralloc_unlock users are readers
// and rcFlushWindowColorBuffer is the writer.
// From the perspective of the host preparing and posting
// buffers, these are indeed read/write operations.
//
// To handle b), we give up on locking when the state is observed
// to be bad. lockState tracks how many color buffer locks there are.
// If lockState < 0, it means we definitely have an unlock before lock
// sort of situation, and should give up.
lockState = 0;
}
// lockColorBufferPrepare is designed to handle
// gralloc_lock/unlock requests, and uses the read lock.
// When rcFlushWindowColorBuffer is called (when frames are posted),
// we use the write lock (see GrallocSyncPostLock).
void lockColorBufferPrepare() {
int newLockState = ++lockState;
if (mEnabled && newLockState == 1) {
mGrallocColorBufferLock.lockRead();
} else if (mEnabled) {
GRSYNC_DPRINT("warning: recursive/multiple locks from guest!");
}
}
void unlockColorBufferPrepare() {
int newLockState = --lockState;
if (mEnabled && newLockState == 0) mGrallocColorBufferLock.unlockRead();
}
android::base::ReadWriteLock mGrallocColorBufferLock;
private:
bool mEnabled;
std::atomic<int> lockState;
DISALLOW_COPY_ASSIGN_AND_MOVE(GrallocSync);
};
class GrallocSyncPostLock : public android::base::AutoWriteLock {
public:
GrallocSyncPostLock(GrallocSync& grallocsync) :
android::base::AutoWriteLock(grallocsync.mGrallocColorBufferLock) { }
};
static ::emugl::LazyInstance<GrallocSync> sGrallocSync = LAZY_INSTANCE_INIT;
static const GLint rendererVersion = 1;
// GLAsyncSwap version history:
// "ANDROID_EMU_NATIVE_SYNC": original version
// "ANDROIDEMU_native_sync_v2": +cleanup of sync objects
// (We need all the different strings to not be prefixes of any other
// due to how they are checked for in the GL extensions on the guest)
static android::base::StringView kAsyncSwapStr = "ANDROID_EMU_native_sync_v2";
static void rcTriggerWait(uint64_t glsync_ptr,
uint64_t thread_ptr,
uint64_t timeline);
static GLint rcGetRendererVersion()
{
emugl_sync_register_trigger_wait(rcTriggerWait);
sGrallocSync.ptr();
return rendererVersion;
}
static EGLint rcGetEGLVersion(EGLint* major, EGLint* minor)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return EGL_FALSE;
}
*major = (EGLint)fb->getCaps().eglMajor;
*minor = (EGLint)fb->getCaps().eglMinor;
return EGL_TRUE;
}
static EGLint rcQueryEGLString(EGLenum name, void* buffer, EGLint bufferSize)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
const char *str = s_egl.eglQueryString(fb->getDisplay(), name);
if (!str) {
return 0;
}
std::string eglStr(str);
int len = eglStr.size() + 1;
if (!buffer || len > bufferSize) {
return -len;
}
strcpy((char *)buffer, eglStr.c_str());
return len;
}
static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize)
{
RenderThreadInfo *tInfo = RenderThreadInfo::get();
// whatever we end up returning,
// it will have a terminating \0,
// so account for it here.
std::string glStr;
if (tInfo && tInfo->currContext.get()) {
const char *str = nullptr;
if (tInfo->currContext->isGL2()) {
str = (const char *)s_gles2.glGetString(name);
}
else {
str = (const char *)s_gles1.glGetString(name);
}
if (str) {
glStr += str;
}
}
// We add the maximum supported GL protocol number into GL_EXTENSIONS
bool isChecksumEnabled =
emugl_feature_is_enabled(android::featurecontrol::GLPipeChecksum);
bool asyncSwapEnabled =
emugl_feature_is_enabled(android::featurecontrol::GLAsyncSwap) &&
emugl_sync_device_exists();
if (isChecksumEnabled && name == GL_EXTENSIONS) {
glStr += ChecksumCalculatorThreadInfo::getMaxVersionString();
glStr += " ";
}
if (asyncSwapEnabled && name == GL_EXTENSIONS) {
glStr += kAsyncSwapStr;
glStr += " ";
}
int nextBufferSize = glStr.size() + 1;
if (!buffer || nextBufferSize > bufferSize) {
return -nextBufferSize;
}
snprintf((char *)buffer, nextBufferSize, "%s", glStr.c_str());
return nextBufferSize;
}
static EGLint rcGetNumConfigs(uint32_t* p_numAttribs)
{
int numConfigs = 0, numAttribs = 0;
FrameBuffer::getFB()->getConfigs()->getPackInfo(&numConfigs, &numAttribs);
if (p_numAttribs) {
*p_numAttribs = static_cast<uint32_t>(numAttribs);
}
return numConfigs;
}
static EGLint rcGetConfigs(uint32_t bufSize, GLuint* buffer)
{
GLuint bufferSize = (GLuint)bufSize;
return FrameBuffer::getFB()->getConfigs()->packConfigs(bufferSize, buffer);
}
static EGLint rcChooseConfig(EGLint *attribs,
uint32_t attribs_size,
uint32_t *configs,
uint32_t configs_size)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb || attribs_size==0) {
return 0;
}
return fb->getConfigs()->chooseConfig(
attribs, (EGLint*)configs, (EGLint)configs_size);
}
static EGLint rcGetFBParam(EGLint param)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
EGLint ret = 0;
switch(param) {
case FB_WIDTH:
ret = fb->getWidth();
break;
case FB_HEIGHT:
ret = fb->getHeight();
break;
case FB_XDPI:
ret = 72; // XXX: should be implemented
break;
case FB_YDPI:
ret = 72; // XXX: should be implemented
break;
case FB_FPS:
ret = 60;
break;
case FB_MIN_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
case FB_MAX_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
default:
break;
}
return ret;
}
static uint32_t rcCreateContext(uint32_t config,
uint32_t share, uint32_t glVersion)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
// To make it consistent with the guest, create GLES2 context when GL
// version==2 or 3
HandleType ret = fb->createRenderContext(config, share, glVersion == 2 || glVersion == 3);
return ret;
}
static void rcDestroyContext(uint32_t context)
{
SyncThread::destroySyncThread();
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->DestroyRenderContext(context);
}
static uint32_t rcCreateWindowSurface(uint32_t config,
uint32_t width, uint32_t height)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->createWindowSurface(config, width, height);
}
static void rcDestroyWindowSurface(uint32_t windowSurface)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->DestroyWindowSurface( windowSurface );
}
static uint32_t rcCreateColorBuffer(uint32_t width,
uint32_t height, GLenum internalFormat)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->createColorBuffer(width, height, internalFormat);
}
static int rcOpenColorBuffer2(uint32_t colorbuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return -1;
}
return fb->openColorBuffer( colorbuffer );
}
static void rcOpenColorBuffer(uint32_t colorbuffer)
{
(void) rcOpenColorBuffer2(colorbuffer);
}
static void rcCloseColorBuffer(uint32_t colorbuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->closeColorBuffer( colorbuffer );
}
static int rcFlushWindowColorBuffer(uint32_t windowSurface)
{
GRSYNC_DPRINT("waiting for gralloc cb lock");
GrallocSyncPostLock lock(sGrallocSync.get());
GRSYNC_DPRINT("lock gralloc cb lock {");
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
GRSYNC_DPRINT("unlock gralloc cb lock");
return -1;
}
if (!fb->flushWindowSurfaceColorBuffer(windowSurface)) {
GRSYNC_DPRINT("unlock gralloc cb lock }");
return -1;
}
GRSYNC_DPRINT("unlock gralloc cb lock }");
return 0;
}
// Note that even though this calls rcFlushWindowColorBuffer,
// the "Async" part is in the return type, which is void
// versus return type int for rcFlushWindowColorBuffer.
//
// The different return type, even while calling the same
// functions internally, will end up making the encoder
// and decoder use a different protocol. This is because
// the encoder generally obeys the following conventions:
//
// - The encoder will immediately send and wait for a command
// result if the return type is not void.
// - The encoder will cache the command in a buffer and send
// at a convenient time if the return type is void.
//
// It can also be expensive performance-wise to trigger
// sending traffic back to the guest. Generally, the more we avoid
// encoding commands that perform two-way traffic, the better.
//
// Hence, |rcFlushWindowColorBufferAsync| will avoid extra traffic;
// with return type void,
// the guest will not wait until this function returns,
// nor will it immediately send the command,
// resulting in more asynchronous behavior.
static void rcFlushWindowColorBufferAsync(uint32_t windowSurface)
{
rcFlushWindowColorBuffer(windowSurface);
}
static void rcSetWindowColorBuffer(uint32_t windowSurface,
uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->setWindowSurfaceColorBuffer(windowSurface, colorBuffer);
}
static EGLint rcMakeCurrent(uint32_t context,
uint32_t drawSurf, uint32_t readSurf)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return EGL_FALSE;
}
bool ret = fb->bindContext(context, drawSurf, readSurf);
return (ret ? EGL_TRUE : EGL_FALSE);
}
static void rcFBPost(uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->post(colorBuffer);
}
static void rcFBSetSwapInterval(EGLint interval)
{
// XXX: TBD - should be implemented
}
static void rcBindTexture(uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->bindColorBufferToTexture(colorBuffer);
}
static void rcBindRenderbuffer(uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->bindColorBufferToRenderbuffer(colorBuffer);
}
static EGLint rcColorBufferCacheFlush(uint32_t colorBuffer,
EGLint postCount, int forRead)
{
// gralloc_lock() on the guest calls rcColorBufferCacheFlush
GRSYNC_DPRINT("waiting for gralloc cb lock");
sGrallocSync->lockColorBufferPrepare();
GRSYNC_DPRINT("lock gralloc cb lock {");
return 0;
}
static void rcReadColorBuffer(uint32_t colorBuffer,
GLint x, GLint y,
GLint width, GLint height,
GLenum format, GLenum type, void* pixels)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->readColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
}
static int rcUpdateColorBuffer(uint32_t colorBuffer,
GLint x, GLint y,
GLint width, GLint height,
GLenum format, GLenum type, void* pixels)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
GRSYNC_DPRINT("unlock gralloc cb lock");
sGrallocSync->unlockColorBufferPrepare();
return -1;
}
fb->updateColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
GRSYNC_DPRINT("unlock gralloc cb lock");
sGrallocSync->unlockColorBufferPrepare();
return 0;
}
static uint32_t rcCreateClientImage(uint32_t context, EGLenum target, GLuint buffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->createClientImage(context, target, buffer);
}
static int rcDestroyClientImage(uint32_t image)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->destroyClientImage(image);
}
static void rcSelectChecksumHelper(uint32_t protocol, uint32_t reserved) {
ChecksumCalculatorThreadInfo::setVersion(protocol);
}
// |rcTriggerWait| is called from the goldfish sync
// kernel driver whenever a native fence fd is created.
// We will then need to use the host to find out
// when to signal that native fence fd. We use
// SyncThread for that.
//
// The purpose of |rcTriggerWait| is to tell which
// SyncThread which sync object / timeline to signal.
static void rcTriggerWait(uint64_t eglsync_ptr,
uint64_t thread_ptr,
uint64_t timeline) {
FenceSync* fenceSync = (FenceSync*)(uintptr_t)eglsync_ptr;
EGLSYNC_DPRINT("eglsync=0x%llx "
"fenceSync=%p "
"thread_ptr=0x%llx "
"timeline=0x%llx",
eglsync_ptr, fenceSync, thread_ptr, timeline);
SyncThread* syncThread =
reinterpret_cast<SyncThread*>(thread_ptr);
syncThread->triggerWait(fenceSync, timeline);
}
// |rcCreateSyncKHR| implements the guest's
// |eglCreateSyncKHR| by calling the host's implementation
// of |eglCreateSyncKHR|. A SyncThread is also started
// that corresponds to the current rendering thread, for
// purposes of signaling any native fence fd's that
// get created in the guest off the sync object
// created here.
static void rcCreateSyncKHR(EGLenum type,
EGLint* attribs,
uint32_t num_attribs,
int destroy_when_signaled,
uint64_t* eglsync_out,
uint64_t* syncthread_out) {
EGLSYNC_DPRINT("type=0x%x num_attribs=%d",
type, num_attribs);
bool hasNativeFence =
type == EGL_SYNC_NATIVE_FENCE_ANDROID;
FenceSync* fenceSync = new FenceSync(hasNativeFence,
destroy_when_signaled);
// This MUST be present, or we get a deadlock effect.
s_gles2.glFlush();
if (syncthread_out) *syncthread_out =
reinterpret_cast<uint64_t>(SyncThread::getSyncThread());
if (eglsync_out) {
uint64_t res = (uint64_t)(uintptr_t)fenceSync;
*eglsync_out = res;
EGLSYNC_DPRINT("send out eglsync 0x%llx", res);
}
}
// |rcClientWaitSyncKHR| implements |eglClientWaitSyncKHR|
// on the guest through using the host's existing
// |eglClientWaitSyncKHR| implementation, which is done
// through the FenceSync object.
static EGLint rcClientWaitSyncKHR(uint64_t handle,
EGLint flags,
uint64_t timeout) {
RenderThreadInfo *tInfo = RenderThreadInfo::get();
FrameBuffer *fb = FrameBuffer::getFB();
EGLSYNC_DPRINT("handle=0x%lx flags=0x%x timeout=%" PRIu64,
handle, flags, timeout);
FenceSync* fenceSync = (FenceSync*)(uintptr_t)handle;
if (!fenceSync) {
EGLSYNC_DPRINT("fenceSync null, return condition satisfied");
return EGL_CONDITION_SATISFIED_KHR;
}
// Sometimes a gralloc-buffer-only thread is doing stuff with sync.
// This happens all the time with YouTube videos in the browser.
// In this case, create a context on the host just for syncing.
if (!tInfo->currContext) {
uint32_t gralloc_sync_cxt, gralloc_sync_surf;
fb->createTrivialContext(0, // There is no context to share.
&gralloc_sync_cxt,
&gralloc_sync_surf);
fb->bindContext(gralloc_sync_cxt,
gralloc_sync_surf,
gralloc_sync_surf);
// This context is then cleaned up when the render thread exits.
}
return fenceSync->wait(timeout);
}
static int rcDestroySyncKHR(uint64_t handle) {
FenceSync* fenceSync = (FenceSync*)(uintptr_t)handle;
assert(fenceSync);
fenceSync->decRef();
return 0;
}
static void rcSetPuid(uint64_t puid) {
RenderThreadInfo *tInfo = RenderThreadInfo::get();
tInfo->m_puid = puid;
}
void initRenderControlContext(renderControl_decoder_context_t *dec)
{
dec->rcGetRendererVersion = rcGetRendererVersion;
dec->rcGetEGLVersion = rcGetEGLVersion;
dec->rcQueryEGLString = rcQueryEGLString;
dec->rcGetGLString = rcGetGLString;
dec->rcGetNumConfigs = rcGetNumConfigs;
dec->rcGetConfigs = rcGetConfigs;
dec->rcChooseConfig = rcChooseConfig;
dec->rcGetFBParam = rcGetFBParam;
dec->rcCreateContext = rcCreateContext;
dec->rcDestroyContext = rcDestroyContext;
dec->rcCreateWindowSurface = rcCreateWindowSurface;
dec->rcDestroyWindowSurface = rcDestroyWindowSurface;
dec->rcCreateColorBuffer = rcCreateColorBuffer;
dec->rcOpenColorBuffer = rcOpenColorBuffer;
dec->rcCloseColorBuffer = rcCloseColorBuffer;
dec->rcSetWindowColorBuffer = rcSetWindowColorBuffer;
dec->rcFlushWindowColorBuffer = rcFlushWindowColorBuffer;
dec->rcMakeCurrent = rcMakeCurrent;
dec->rcFBPost = rcFBPost;
dec->rcFBSetSwapInterval = rcFBSetSwapInterval;
dec->rcBindTexture = rcBindTexture;
dec->rcBindRenderbuffer = rcBindRenderbuffer;
dec->rcColorBufferCacheFlush = rcColorBufferCacheFlush;
dec->rcReadColorBuffer = rcReadColorBuffer;
dec->rcUpdateColorBuffer = rcUpdateColorBuffer;
dec->rcOpenColorBuffer2 = rcOpenColorBuffer2;
dec->rcCreateClientImage = rcCreateClientImage;
dec->rcDestroyClientImage = rcDestroyClientImage;
dec->rcSelectChecksumHelper = rcSelectChecksumHelper;
dec->rcCreateSyncKHR = rcCreateSyncKHR;
dec->rcClientWaitSyncKHR = rcClientWaitSyncKHR;
dec->rcFlushWindowColorBufferAsync = rcFlushWindowColorBufferAsync;
dec->rcDestroySyncKHR = rcDestroySyncKHR;
dec->rcSetPuid = rcSetPuid;
}