blob: 5d193bfb9cc5da7fe86f3cd338b721a2d1b440e4 [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.
*/
#ifdef GFXSTREAM
#include <atomic>
#include <time.h>
#endif
#include <assert.h>
#include "HostConnection.h"
#include "ThreadInfo.h"
#include "aemu/base/threads/AndroidThread.h"
#include "eglDisplay.h"
#include "eglSync.h"
#include "egl_ftable.h"
#if PLATFORM_SDK_VERSION < 26
#include <cutils/log.h>
#else
#include <log/log.h>
#endif
#include <cutils/properties.h>
#include "goldfish_sync.h"
#include "GLClientState.h"
#include "GLSharedGroup.h"
#include "eglContext.h"
#include "ClientAPIExts.h"
#include "EGLImage.h"
#include "ProcessPipe.h"
#include "profiler.h"
#include <qemu_pipe_bp.h>
#include "GLEncoder.h"
#include "GL2Encoder.h"
#include <GLES3/gl31.h>
#ifdef VIRTIO_GPU
#include <xf86drm.h>
#include <poll.h>
#include "virtgpu_drm.h"
#endif // VIRTIO_GPU
#ifdef GFXSTREAM
#include "aemu/base/Tracing.h"
#endif
#include <cutils/trace.h>
#include <system/window.h>
using android::base::guest::getCurrentThreadId;
#define DEBUG_EGL 0
#if DEBUG_EGL
#define DPRINT(fmt,...) ALOGD("%s: " fmt, __FUNCTION__, ##__VA_ARGS__);
#else
#define DPRINT(...)
#endif
template<typename T>
static T setErrorFunc(GLint error, T returnValue) {
getEGLThreadInfo()->eglError = error;
return returnValue;
}
const char * eglStrError(EGLint err)
{
switch (err){
case EGL_SUCCESS: return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
default: return "UNKNOWN";
}
}
#define LOG_EGL_ERRORS 1
#ifdef LOG_EGL_ERRORS
#define setErrorReturn(error, retVal) \
{ \
ALOGE("tid %lu: %s(%d): error 0x%x (%s)", getCurrentThreadId(), __FUNCTION__, __LINE__, \
error, eglStrError(error)); \
return setErrorFunc(error, retVal); \
}
#define RETURN_ERROR(ret, err) \
ALOGE("tid %lu: %s(%d): error 0x%x (%s)", getCurrentThreadId(), __FUNCTION__, __LINE__, err, \
eglStrError(err)); \
getEGLThreadInfo()->eglError = err; \
return ret;
#else //!LOG_EGL_ERRORS
#define setErrorReturn(error, retVal) return setErrorFunc(error, retVal);
#define RETURN_ERROR(ret,err) \
getEGLThreadInfo()->eglError = err; \
return ret;
#endif //LOG_EGL_ERRORS
#define VALIDATE_CONFIG(cfg,ret) \
if (!s_display.isValidConfig(cfg)) { \
RETURN_ERROR(ret,EGL_BAD_CONFIG); \
}
#define VALIDATE_DISPLAY(dpy,ret) \
if ((dpy) != (EGLDisplay)&s_display) { \
RETURN_ERROR(ret, EGL_BAD_DISPLAY); \
}
#define VALIDATE_DISPLAY_INIT(dpy,ret) \
VALIDATE_DISPLAY(dpy, ret) \
if (!s_display.initialized()) { \
RETURN_ERROR(ret, EGL_NOT_INITIALIZED); \
}
#define DEFINE_HOST_CONNECTION \
HostConnection *hostCon = HostConnection::get(); \
ExtendedRCEncoderContext *rcEnc = (hostCon ? hostCon->rcEncoder() : NULL)
#define DEFINE_AND_VALIDATE_HOST_CONNECTION(ret) \
HostConnection *hostCon = HostConnection::get(); \
if (!hostCon) { \
ALOGE("egl: Failed to get host connection\n"); \
return ret; \
} \
ExtendedRCEncoderContext *rcEnc = hostCon->rcEncoder(); \
if (!rcEnc) { \
ALOGE("egl: Failed to get renderControl encoder context\n"); \
return ret; \
} \
Gralloc *grallocHelper = hostCon->grallocHelper(); \
if (!grallocHelper) { \
ALOGE("egl: Failed to get grallocHelper\n"); \
return ret; \
}
#define DEFINE_AND_VALIDATE_HOST_CONNECTION_FOR_TLS(ret, tls) \
HostConnection *hostCon = HostConnection::getWithThreadInfo(tls); \
if (!hostCon) { \
ALOGE("egl: Failed to get host connection\n"); \
return ret; \
} \
ExtendedRCEncoderContext *rcEnc = hostCon->rcEncoder(); \
if (!rcEnc) { \
ALOGE("egl: Failed to get renderControl encoder context\n"); \
return ret; \
} \
Gralloc const* grallocHelper = hostCon->grallocHelper(); \
if (!grallocHelper) { \
ALOGE("egl: Failed to get grallocHelper\n"); \
return ret; \
}
#define VALIDATE_CONTEXT_RETURN(context,ret) \
if (!(context) || !s_display.isContext((context))) { \
RETURN_ERROR(ret,EGL_BAD_CONTEXT); \
}
#define VALIDATE_SURFACE_RETURN(surface, ret) \
if ((surface) != EGL_NO_SURFACE) { \
if (!s_display.isSurface((surface))) \
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); \
egl_surface_t* s( static_cast<egl_surface_t*>(surface) ); \
if (s->dpy != (EGLDisplay)&s_display) \
setErrorReturn(EGL_BAD_DISPLAY, EGL_FALSE); \
}
// The one and only supported display object.
static eglDisplay s_display;
// Extra defines not in the official EGL spec yet,
// but required in Android CTS.
#define EGL_TIMESTAMPS_ANDROID 0x314D
EGLContext_t::EGLContext_t(EGLDisplay dpy, EGLConfig config, EGLContext_t* shareCtx, int maj, int min) :
dpy(dpy),
config(config),
read(EGL_NO_SURFACE),
draw(EGL_NO_SURFACE),
shareCtx(shareCtx),
rcContext(0),
versionString(NULL),
majorVersion(maj),
minorVersion(min),
vendorString(NULL) ,
rendererString(NULL),
shaderVersionString(NULL),
extensionString(NULL),
deletePending(0),
goldfishSyncFd(-1)
{
DEFINE_HOST_CONNECTION;
switch (rcEnc->getGLESMaxVersion()) {
case GLES_MAX_VERSION_3_0:
deviceMajorVersion = 3;
deviceMinorVersion = 0;
break;
case GLES_MAX_VERSION_3_1:
deviceMajorVersion = 3;
deviceMinorVersion = 1;
break;
case GLES_MAX_VERSION_3_2:
deviceMajorVersion = 3;
deviceMinorVersion = 2;
break;
default:
deviceMajorVersion = 2;
deviceMinorVersion = 0;
break;
}
flags = 0;
clientState = new GLClientState(majorVersion, minorVersion);
if (shareCtx)
sharedGroup = shareCtx->getSharedGroup();
else
sharedGroup = GLSharedGroupPtr(new GLSharedGroup());
assert(dpy == (EGLDisplay)&s_display);
s_display.onCreateContext((EGLContext)this);
};
int EGLContext_t::getGoldfishSyncFd() {
if (goldfishSyncFd < 0) {
goldfishSyncFd = goldfish_sync_open();
}
return goldfishSyncFd;
}
EGLContext_t::~EGLContext_t()
{
if (goldfishSyncFd > 0) {
goldfish_sync_close(goldfishSyncFd);
goldfishSyncFd = -1;
}
assert(dpy == (EGLDisplay)&s_display);
s_display.onDestroyContext((EGLContext)this);
delete clientState;
delete [] versionString;
delete [] vendorString;
delete [] rendererString;
delete [] shaderVersionString;
delete [] extensionString;
}
uint64_t currGuestTimeNs() {
struct timespec ts;
#ifdef __APPLE__
clock_gettime(CLOCK_REALTIME, &ts);
#else
clock_gettime(CLOCK_BOOTTIME, &ts);
#endif
uint64_t res = (uint64_t)(ts.tv_sec * 1000000000ULL + ts.tv_nsec);
return res;
}
struct app_time_metric_t {
uint64_t lastLogTime;
uint64_t lastSwapBuffersReturnTime;
unsigned int numSamples;
uint64_t totalAppTime;
uint64_t minAppTime;
uint64_t maxAppTime;
app_time_metric_t() :
lastLogTime(0),
lastSwapBuffersReturnTime(0),
numSamples(0),
totalAppTime(0),
minAppTime(0),
maxAppTime(0)
{
}
void onSwapBuffersReturn() {
lastSwapBuffersReturnTime = currGuestTimeNs();
}
static float ns2ms(uint64_t ns) {
return (float)ns / 1000000.0;
}
void onQueueBufferReturn() {
if(lastSwapBuffersReturnTime == 0) {
// First swapBuffers call, or last call failed.
return;
}
uint64_t now = currGuestTimeNs();
uint64_t appTime = now - lastSwapBuffersReturnTime;
if(numSamples == 0) {
minAppTime = appTime;
maxAppTime = appTime;
}
else {
minAppTime = fmin(minAppTime, appTime);
maxAppTime = fmax(maxAppTime, appTime);
}
totalAppTime += appTime;
numSamples++;
// Reset so we don't record a bad sample if swapBuffers fails
lastSwapBuffersReturnTime = 0;
if(lastLogTime == 0) {
lastLogTime = now;
return;
}
// Log/reset once every second
if(now - lastLogTime > 1000000000) {
float avgMs = ns2ms(totalAppTime) / numSamples;
float minMs = ns2ms(minAppTime);
float maxMs = ns2ms(maxAppTime);
// B* needs the following log.
ALOGD("app_time_stats: avg=%0.2fms min=%0.2fms max=%0.2fms count=%u", avgMs, minMs, maxMs, numSamples);
totalAppTime = 0;
minAppTime = 0;
maxAppTime = 0;
numSamples = 0;
lastLogTime = now;
}
}
};
// ----------------------------------------------------------------------------
//egl_surface_t
//we don't need to handle depth since it's handled when window created on the host
struct egl_surface_t {
EGLDisplay dpy;
EGLConfig config;
egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfaceType);
virtual ~egl_surface_t();
virtual void setSwapInterval(int interval) = 0;
virtual EGLBoolean swapBuffers() = 0;
EGLint getSwapBehavior() const;
uint32_t getRcSurface() { return rcSurface; }
EGLint getSurfaceType() { return surfaceType; }
EGLint getWidth(){ return width; }
EGLint getHeight(){ return height; }
EGLint getNativeWidth(){ return nativeWidth; }
EGLint getNativeHeight(){ return nativeHeight; }
void setTextureFormat(EGLint _texFormat) { texFormat = _texFormat; }
EGLint getTextureFormat() { return texFormat; }
void setTextureTarget(EGLint _texTarget) { texTarget = _texTarget; }
EGLint getTextureTarget() { return texTarget; }
virtual void setCollectingTimestamps(EGLint) { }
virtual EGLint isCollectingTimestamps() const { return EGL_FALSE; }
EGLint deletePending;
void setIsCurrent(bool isCurrent) { mIsCurrent = isCurrent; }
bool isCurrent() const { return mIsCurrent;}
private:
//
//Surface attributes
//
EGLint width;
EGLint height;
EGLint texFormat;
EGLint texTarget;
// Width of the actual window being presented (not the EGL texture)
// Give it some default values.
int nativeWidth;
int nativeHeight;
bool mIsCurrent;
protected:
void setWidth(EGLint w) { width = w; }
void setHeight(EGLint h) { height = h; }
void setNativeWidth(int w) { nativeWidth = w; }
void setNativeHeight(int h) { nativeHeight = h; }
EGLint surfaceType;
uint32_t rcSurface; //handle to surface created via remote control
app_time_metric_t appTimeMetric;
};
egl_surface_t::egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfaceType)
: dpy(dpy), config(config), deletePending(0), mIsCurrent(false),
surfaceType(surfaceType), rcSurface(0)
{
width = 0;
height = 0;
// prevent div by 0 in EGL_(HORIZONTAL|VERTICAL)_RESOLUTION queries.
nativeWidth = 1;
nativeHeight = 1;
texFormat = EGL_NO_TEXTURE;
texTarget = EGL_NO_TEXTURE;
assert(dpy == (EGLDisplay)&s_display);
s_display.onCreateSurface((EGLSurface)this);
}
EGLint egl_surface_t::getSwapBehavior() const {
return EGL_BUFFER_PRESERVED;
}
egl_surface_t::~egl_surface_t()
{
assert(dpy == (EGLDisplay)&s_display);
s_display.onDestroySurface((EGLSurface)this);
}
// ----------------------------------------------------------------------------
// egl_window_surface_t
struct egl_window_surface_t : public egl_surface_t {
static egl_window_surface_t* create(
EGLDisplay dpy, EGLConfig config, EGLint surfType,
ANativeWindow* window);
virtual ~egl_window_surface_t();
virtual void setSwapInterval(int interval);
virtual EGLBoolean swapBuffers();
virtual void setCollectingTimestamps(EGLint collect)
override { collectingTimestamps = (collect == EGL_TRUE) ? true : false; }
virtual EGLint isCollectingTimestamps() const override { return collectingTimestamps ? EGL_TRUE : EGL_FALSE; }
private:
egl_window_surface_t(
EGLDisplay dpy, EGLConfig config, EGLint surfType,
ANativeWindow* window);
EGLBoolean init();
ANativeWindow* nativeWindow;
android_native_buffer_t* buffer;
bool collectingTimestamps;
};
egl_window_surface_t::egl_window_surface_t (
EGLDisplay dpy, EGLConfig config, EGLint surfType,
ANativeWindow* window)
: egl_surface_t(dpy, config, surfType),
nativeWindow(window),
buffer(NULL),
collectingTimestamps(false)
{
// keep a reference on the window
nativeWindow->common.incRef(&nativeWindow->common);
}
EGLBoolean egl_window_surface_t::init()
{
#ifndef HOST_BUILD
int consumerUsage = 0;
if (nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage) != 0) {
setErrorReturn(EGL_BAD_ALLOC, EGL_FALSE);
} else {
int producerUsage = GRALLOC_USAGE_HW_RENDER;
native_window_set_usage(nativeWindow, consumerUsage | producerUsage);
}
#endif
if (nativeWindow->dequeueBuffer_DEPRECATED(nativeWindow, &buffer) != 0) {
setErrorReturn(EGL_BAD_ALLOC, EGL_FALSE);
}
setWidth(buffer->width);
setHeight(buffer->height);
int nativeWidth, nativeHeight;
nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &nativeWidth);
nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &nativeHeight);
setNativeWidth(nativeWidth);
setNativeHeight(nativeHeight);
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
rcSurface = rcEnc->rcCreateWindowSurface(rcEnc, (uintptr_t)s_display.getIndexOfConfig(config),
getWidth(), getHeight());
if (!rcSurface) {
ALOGE("rcCreateWindowSurface returned 0");
return EGL_FALSE;
}
rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface,
grallocHelper->getHostHandle(buffer->handle));
return EGL_TRUE;
}
egl_window_surface_t* egl_window_surface_t::create(
EGLDisplay dpy, EGLConfig config, EGLint surfType,
ANativeWindow* window)
{
egl_window_surface_t* wnd = new egl_window_surface_t(
dpy, config, surfType, window);
if (wnd && !wnd->init()) {
delete wnd;
wnd = NULL;
}
return wnd;
}
egl_window_surface_t::~egl_window_surface_t() {
DEFINE_HOST_CONNECTION;
if (rcSurface && rcEnc) {
rcEnc->rcDestroyWindowSurface(rcEnc, rcSurface);
}
if (buffer) {
nativeWindow->cancelBuffer_DEPRECATED(nativeWindow, buffer);
}
nativeWindow->common.decRef(&nativeWindow->common);
}
void egl_window_surface_t::setSwapInterval(int interval)
{
nativeWindow->setSwapInterval(nativeWindow, interval);
}
// createNativeSync() creates an OpenGL sync object on the host
// using rcCreateSyncKHR. If necessary, a native fence FD will
// also be created through the goldfish sync device.
// Returns a handle to the host-side FenceSync object.
static uint64_t createNativeSync(EGLenum type,
const EGLint* attrib_list,
int num_actual_attribs,
bool destroy_when_signaled,
int fd_in,
int* fd_out) {
DEFINE_HOST_CONNECTION;
uint64_t sync_handle;
uint64_t thread_handle;
EGLint* actual_attribs =
(EGLint*)(num_actual_attribs == 0 ? NULL : attrib_list);
rcEnc->rcCreateSyncKHR(rcEnc, type,
actual_attribs,
num_actual_attribs * sizeof(EGLint),
destroy_when_signaled,
&sync_handle,
&thread_handle);
if (type == EGL_SYNC_NATIVE_FENCE_ANDROID && fd_in < 0) {
int queue_work_err =
goldfish_sync_queue_work(
getEGLThreadInfo()->currentContext->getGoldfishSyncFd(),
sync_handle,
thread_handle,
fd_out);
(void)queue_work_err;
DPRINT("got native fence fd=%d queue_work_err=%d",
*fd_out, queue_work_err);
}
return sync_handle;
}
// our cmd
#define VIRTIO_GPU_NATIVE_SYNC_CREATE_EXPORT_FD 0x9000
#define VIRTIO_GPU_NATIVE_SYNC_CREATE_IMPORT_FD 0x9001
// createNativeSync_virtioGpu()
// creates an OpenGL sync object on the host
// using rcCreateSyncKHR.
// If necessary, a native fence FD will be exported or imported.
// Returns a handle to the host-side FenceSync object.
static uint64_t createNativeSync_virtioGpu(
EGLenum type,
const EGLint* attrib_list,
int num_actual_attribs,
bool destroy_when_signaled,
int fd_in,
int* fd_out) {
#ifndef VIRTIO_GPU
ALOGE("%s: Error: called with no virtio-gpu support built in\n", __func__);
return 0;
#else
DEFINE_HOST_CONNECTION;
uint64_t sync_handle;
uint64_t thread_handle;
EGLint* actual_attribs =
(EGLint*)(num_actual_attribs == 0 ? NULL : attrib_list);
// Create a normal sync obj
rcEnc->rcCreateSyncKHR(rcEnc, type,
actual_attribs,
num_actual_attribs * sizeof(EGLint),
destroy_when_signaled,
&sync_handle,
&thread_handle);
// Import fence fd; dup and close
if (type == EGL_SYNC_NATIVE_FENCE_ANDROID && fd_in >= 0) {
int importedFd = dup(fd_in);
if (importedFd < 0) {
ALOGE("%s: error: failed to dup imported fd. original: %d errno %d\n",
__func__, fd_in, errno);
}
*fd_out = importedFd;
if (close(fd_in)) {
ALOGE("%s: error: failed to close imported fd. original: %d errno %d\n",
__func__, fd_in, errno);
}
} else if (type == EGL_SYNC_NATIVE_FENCE_ANDROID && fd_in < 0) {
// Export fence fd
uint32_t sync_handle_lo = (uint32_t)sync_handle;
uint32_t sync_handle_hi = (uint32_t)(sync_handle >> 32);
uint32_t cmdDwords[3] = {
VIRTIO_GPU_NATIVE_SYNC_CREATE_EXPORT_FD,
sync_handle_lo,
sync_handle_hi,
};
drm_virtgpu_execbuffer createSyncExport = {
.flags = VIRTGPU_EXECBUF_FENCE_FD_OUT,
.size = 3 * sizeof(uint32_t),
.command = (uint64_t)(cmdDwords),
.bo_handles = 0,
.num_bo_handles = 0,
.fence_fd = -1,
};
int queue_work_err =
drmIoctl(
hostCon->getRendernodeFd(),
DRM_IOCTL_VIRTGPU_EXECBUFFER, &createSyncExport);
if (queue_work_err) {
ERR("%s: failed with %d executing command buffer (%s)", __func__,
queue_work_err, strerror(errno));
return 0;
}
*fd_out = createSyncExport.fence_fd;
DPRINT("virtio-gpu: got native fence fd=%d queue_work_err=%d",
*fd_out, queue_work_err);
}
return sync_handle;
#endif
}
// createGoldfishOpenGLNativeSync() is for creating host-only sync objects
// that are needed by only this goldfish opengl driver,
// such as in swapBuffers().
// The guest will not see any of these, and these sync objects will be
// destroyed on the host when signaled.
// A native fence FD is possibly returned.
static void createGoldfishOpenGLNativeSync(int* fd_out) {
createNativeSync(EGL_SYNC_NATIVE_FENCE_ANDROID,
NULL /* empty attrib list */,
0 /* 0 attrib count */,
true /* destroy when signaled. this is host-only
and there will only be one waiter */,
-1 /* we want a new fd */,
fd_out);
}
struct FrameTracingState {
uint32_t frameNumber = 0;
bool tracingEnabled = false;
void onSwapBuffersSuccesful(ExtendedRCEncoderContext* rcEnc) {
#ifdef GFXSTREAM
// edge trigger
if (android::base::isTracingEnabled() && !tracingEnabled) {
if (rcEnc->hasHostSideTracing()) {
rcEnc->rcSetTracingForPuid(rcEnc, getPuid(), 1, currGuestTimeNs());
}
}
if (!android::base::isTracingEnabled() && tracingEnabled) {
if (rcEnc->hasHostSideTracing()) {
rcEnc->rcSetTracingForPuid(rcEnc, getPuid(), 0, currGuestTimeNs());
}
}
tracingEnabled = android::base::isTracingEnabled();
#endif
++frameNumber;
}
};
static FrameTracingState sFrameTracingState;
static void sFlushBufferAndCreateFence(
HostConnection* hostCon, ExtendedRCEncoderContext* rcEnc, uint32_t rcSurface, uint32_t frameNumber, int* presentFenceFd) {
atrace_int(ATRACE_TAG_GRAPHICS, "gfxstreamFrameNumber", (int32_t)frameNumber);
if (rcEnc->hasHostSideTracing()) {
rcEnc->rcFlushWindowColorBufferAsyncWithFrameNumber(rcEnc, rcSurface, frameNumber);
} else {
rcEnc->rcFlushWindowColorBufferAsync(rcEnc, rcSurface);
}
if (rcEnc->hasVirtioGpuNativeSync()) {
createNativeSync_virtioGpu(EGL_SYNC_NATIVE_FENCE_ANDROID,
NULL /* empty attrib list */,
0 /* 0 attrib count */,
true /* destroy when signaled. this is host-only
and there will only be one waiter */,
-1 /* we want a new fd */,
presentFenceFd);
} else if (rcEnc->hasNativeSync()) {
createGoldfishOpenGLNativeSync(presentFenceFd);
} else {
// equivalent to glFinish if no native sync
eglWaitClient();
}
}
EGLBoolean egl_window_surface_t::swapBuffers()
{
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
// Follow up flushWindowColorBuffer with a fence command.
// When the fence command finishes,
// we're sure that the buffer on the host
// has been blitted.
//
// |presentFenceFd| guards the presentation of the
// current frame with a goldfish sync fence fd.
//
// When |presentFenceFd| is signaled, the recipient
// of the buffer that was sent through queueBuffer
// can be sure that the buffer is current.
//
// If we don't take care of this synchronization,
// an old frame can be processed by surfaceflinger,
// resulting in out of order frames.
int presentFenceFd = -1;
if (buffer == NULL) {
ALOGE("egl_window_surface_t::swapBuffers called with NULL buffer");
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
}
sFlushBufferAndCreateFence(
hostCon, rcEnc, rcSurface,
sFrameTracingState.frameNumber, &presentFenceFd);
DPRINT("queueBuffer with fence %d", presentFenceFd);
nativeWindow->queueBuffer(nativeWindow, buffer, presentFenceFd);
appTimeMetric.onQueueBufferReturn();
DPRINT("calling dequeueBuffer...");
int acquireFenceFd = -1;
if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &acquireFenceFd)) {
buffer = NULL;
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
}
DPRINT("dequeueBuffer with fence %d", acquireFenceFd);
if (acquireFenceFd > 0) {
close(acquireFenceFd);
}
rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface,
grallocHelper->getHostHandle(buffer->handle));
setWidth(buffer->width);
setHeight(buffer->height);
sFrameTracingState.onSwapBuffersSuccesful(rcEnc);
appTimeMetric.onSwapBuffersReturn();
return EGL_TRUE;
}
// ----------------------------------------------------------------------------
//egl_pbuffer_surface_t
struct egl_pbuffer_surface_t : public egl_surface_t {
static egl_pbuffer_surface_t* create(EGLDisplay dpy, EGLConfig config,
EGLint surfType, int32_t w, int32_t h, GLenum pixelFormat);
virtual ~egl_pbuffer_surface_t();
virtual void setSwapInterval(int interval) { (void)interval; }
virtual EGLBoolean swapBuffers() { return EGL_TRUE; }
uint32_t getRcColorBuffer() { return rcColorBuffer; }
private:
egl_pbuffer_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfType,
int32_t w, int32_t h);
EGLBoolean init(GLenum format);
uint32_t rcColorBuffer;
QEMU_PIPE_HANDLE refcountPipeFd;
};
egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy, EGLConfig config,
EGLint surfType, int32_t w, int32_t h)
: egl_surface_t(dpy, config, surfType),
rcColorBuffer(0), refcountPipeFd(QEMU_PIPE_INVALID_HANDLE)
{
setWidth(w);
setHeight(h);
}
egl_pbuffer_surface_t::~egl_pbuffer_surface_t()
{
DEFINE_HOST_CONNECTION;
if (rcEnc) {
if (rcColorBuffer){
if(qemu_pipe_valid(refcountPipeFd)) {
qemu_pipe_close(refcountPipeFd);
} else {
rcEnc->rcCloseColorBuffer(rcEnc, rcColorBuffer);
}
}
if (rcSurface) rcEnc->rcDestroyWindowSurface(rcEnc, rcSurface);
}
}
// Destroy a pending surface and set it to NULL.
static void s_destroyPendingSurfaceAndSetNull(EGLSurface* surface) {
if (!surface)
return;
if (!s_display.isSurface(*surface)) {
*surface = NULL;
return;
}
egl_surface_t* surf = static_cast<egl_surface_t *>(*surface);
if (surf && surf->deletePending) {
delete surf;
*surface = NULL;
}
}
static void s_destroyPendingSurfacesInContext(EGLContext_t* context) {
if (context->read == context->draw) {
// If they are the same, delete it only once
s_destroyPendingSurfaceAndSetNull(&context->draw);
if (context->draw == NULL) {
context->read = NULL;
}
} else {
s_destroyPendingSurfaceAndSetNull(&context->draw);
s_destroyPendingSurfaceAndSetNull(&context->read);
}
}
EGLBoolean egl_pbuffer_surface_t::init(GLenum pixelFormat)
{
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
rcSurface = rcEnc->rcCreateWindowSurface(rcEnc, (uintptr_t)s_display.getIndexOfConfig(config),
getWidth(), getHeight());
if (!rcSurface) {
ALOGE("rcCreateWindowSurface returned 0");
return EGL_FALSE;
}
rcColorBuffer = grallocHelper->createColorBuffer(rcEnc, getWidth(), getHeight(), pixelFormat);
if (!rcColorBuffer) {
ALOGE("rcCreateColorBuffer returned 0");
return EGL_FALSE;
} else {
refcountPipeFd = qemu_pipe_open("refcount");
//Send color buffer handle in case RefCountPipe feature is turned on.
if (qemu_pipe_valid(refcountPipeFd)) {
qemu_pipe_write(refcountPipeFd, &rcColorBuffer, 4);
}
}
rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface, rcColorBuffer);
return EGL_TRUE;
}
egl_pbuffer_surface_t* egl_pbuffer_surface_t::create(EGLDisplay dpy,
EGLConfig config, EGLint surfType, int32_t w, int32_t h,
GLenum pixelFormat)
{
egl_pbuffer_surface_t* pb = new egl_pbuffer_surface_t(dpy, config, surfType,
w, h);
if (pb && !pb->init(pixelFormat)) {
delete pb;
pb = NULL;
}
return pb;
}
// Required for Skia.
static const char kOESEGLImageExternalEssl3[] = "GL_OES_EGL_image_external_essl3";
static bool sWantES30OrAbove(const char* exts) {
if (strstr(exts, kGLESMaxVersion_3_0) ||
strstr(exts, kGLESMaxVersion_3_1) ||
strstr(exts, kGLESMaxVersion_3_2)) {
return true;
}
return false;
}
static std::vector<std::string> getExtStringArray() {
std::vector<std::string> res;
EGLThreadInfo *tInfo = getEGLThreadInfo();
if (!tInfo || !tInfo->currentContext) {
return res;
}
if (tInfo->currentContext->extensionStringArray.size() > 0) {
return tInfo->currentContext->extensionStringArray;
}
#define GL_EXTENSIONS 0x1F03
DEFINE_AND_VALIDATE_HOST_CONNECTION(res);
char *hostStr = NULL;
int n = rcEnc->rcGetGLString(rcEnc, GL_EXTENSIONS, NULL, 0);
if (n < 0) {
hostStr = new char[-n+1];
n = rcEnc->rcGetGLString(rcEnc, GL_EXTENSIONS, hostStr, -n);
if (n <= 0) {
delete [] hostStr;
hostStr = NULL;
}
}
// push guest strings
res.push_back("GL_EXT_robustness");
if (!hostStr || !strlen(hostStr)) { return res; }
// find the number of extensions
int extStart = 0;
int extEnd = 0;
if (sWantES30OrAbove(hostStr) &&
!strstr(hostStr, kOESEGLImageExternalEssl3)) {
res.push_back(kOESEGLImageExternalEssl3);
}
const int hostStrLen = strlen(hostStr);
while (extEnd < hostStrLen) {
if (hostStr[extEnd] == ' ') {
int extSz = extEnd - extStart;
res.push_back(std::string(hostStr + extStart, extSz));
extStart = extEnd + 1;
}
extEnd++;
}
tInfo->currentContext->extensionStringArray = res;
delete [] hostStr;
return res;
}
static const char *getGLString(int glEnum)
{
EGLThreadInfo *tInfo = getEGLThreadInfo();
if (!tInfo || !tInfo->currentContext) {
return NULL;
}
const char** strPtr = NULL;
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
#define GL_EXTENSIONS 0x1F03
switch(glEnum) {
case GL_VERSION:
strPtr = &tInfo->currentContext->versionString;
break;
case GL_VENDOR:
strPtr = &tInfo->currentContext->vendorString;
break;
case GL_RENDERER:
strPtr = &tInfo->currentContext->rendererString;
break;
case GL_SHADING_LANGUAGE_VERSION:
strPtr = &tInfo->currentContext->shaderVersionString;
break;
case GL_EXTENSIONS:
strPtr = &tInfo->currentContext->extensionString;
break;
}
if (!strPtr) {
return NULL;
}
if (*strPtr) {
return *strPtr;
}
char* hostStr = NULL;
if (glEnum == GL_EXTENSIONS) {
std::vector<std::string> exts = getExtStringArray();
int totalSz = 1; // null terminator
for (unsigned int i = 0; i < exts.size(); i++) {
totalSz += exts[i].size() + 1; // for space
}
if (totalSz == 1) return NULL;
hostStr = new char[totalSz];
memset(hostStr, 0, totalSz);
char* current = hostStr;
for (unsigned int i = 0; i < exts.size(); i++) {
memcpy(current, exts[i].c_str(), exts[i].size());
current += exts[i].size();
*current = ' ';
++current;
}
} else {
//
// first query of that string - need to query host
//
DEFINE_AND_VALIDATE_HOST_CONNECTION(NULL);
int n = rcEnc->rcGetGLString(rcEnc, glEnum, NULL, 0);
if (n < 0) {
hostStr = new char[-n+1];
n = rcEnc->rcGetGLString(rcEnc, glEnum, hostStr, -n);
if (n <= 0) {
delete [] hostStr;
hostStr = NULL;
}
}
}
//
// keep the string in the context and return its value
//
*strPtr = hostStr;
return hostStr;
}
// ----------------------------------------------------------------------------
// Note: C99 syntax was tried here but does not work for all compilers.
static EGLClient_eglInterface s_eglIface = {
getThreadInfo: getEGLThreadInfo,
getGLString: getGLString,
};
#define DBG_FUNC DBG("%s\n", __FUNCTION__)
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id)
{
//
// we support only EGL_DEFAULT_DISPLAY.
//
if (display_id != EGL_DEFAULT_DISPLAY) {
return EGL_NO_DISPLAY;
}
return (EGLDisplay)&s_display;
}
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
{
VALIDATE_DISPLAY(dpy,EGL_FALSE);
if (!s_display.initialize(&s_eglIface)) {
return EGL_FALSE;
}
if (major!=NULL)
*major = s_display.getVersionMajor();
if (minor!=NULL)
*minor = s_display.getVersionMinor();
try_register_goldfish_perfetto();
return EGL_TRUE;
}
EGLBoolean eglTerminate(EGLDisplay dpy)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
s_display.terminate();
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
rcEnc->rcGetRendererVersion(rcEnc);
return EGL_TRUE;
}
EGLint eglGetError()
{
EGLint error = getEGLThreadInfo()->eglError;
getEGLThreadInfo()->eglError = EGL_SUCCESS;
return error;
}
__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
{
// search in EGL function table
for (int i=0; i<egl_num_funcs; i++) {
if (!strcmp(egl_funcs_by_name[i].name, procname)) {
return (__eglMustCastToProperFunctionPointerType)egl_funcs_by_name[i].proc;
}
}
// look in gles client api's extensions table
return (__eglMustCastToProperFunctionPointerType)ClientAPIExts::getProcAddress(procname);
// Fail - function not found.
return NULL;
}
const char* eglQueryString(EGLDisplay dpy, EGLint name)
{
// EGL_BAD_DISPLAY is generated if display is not an EGL display connection, unless display is
// EGL_NO_DISPLAY and name is EGL_EXTENSIONS.
if (dpy || name != EGL_EXTENSIONS) {
VALIDATE_DISPLAY_INIT(dpy, NULL);
}
return s_display.queryString(name);
}
EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
if(!num_config) {
RETURN_ERROR(EGL_FALSE,EGL_BAD_PARAMETER);
}
GLint numConfigs = s_display.getNumConfigs();
if (!configs) {
*num_config = numConfigs;
return EGL_TRUE;
}
EGLint i;
for (i = 0 ; i < numConfigs && i < config_size ; i++) {
*configs++ = (EGLConfig)(uintptr_t)s_display.getConfigAtIndex(i);
}
*num_config = i;
return EGL_TRUE;
}
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
if (!num_config) {
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
int attribs_size = 0;
EGLint backup_attribs[1];
if (attrib_list) {
const EGLint * attrib_p = attrib_list;
while (attrib_p[0] != EGL_NONE) {
attribs_size += 2;
attrib_p += 2;
}
attribs_size++; //for the terminating EGL_NONE
} else {
attribs_size = 1;
backup_attribs[0] = EGL_NONE;
attrib_list = backup_attribs;
}
uint32_t* tempConfigs[config_size];
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
*num_config = rcEnc->rcChooseConfig(rcEnc, (EGLint*)attrib_list,
attribs_size * sizeof(EGLint), (uint32_t*)tempConfigs, config_size);
if (*num_config < 0) {
EGLint err = -(*num_config);
*num_config = 0;
switch (err) {
case EGL_BAD_ATTRIBUTE:
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE);
default:
return EGL_FALSE;
}
}
if (configs!=NULL) {
EGLint i=0;
for (i=0;i<(*num_config);i++) {
EGLConfig guestConfig = s_display.getConfigAtIndex(*((uint32_t*)tempConfigs+i));
configs[i] = guestConfig;
}
}
return EGL_TRUE;
}
EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_CONFIG(config, EGL_FALSE);
if (s_display.getConfigAttrib(config, attribute, value))
{
return EGL_TRUE;
}
else
{
DPRINT("%s: bad attrib 0x%x", __FUNCTION__, attribute);
RETURN_ERROR(EGL_FALSE, EGL_BAD_ATTRIBUTE);
}
}
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list)
{
(void)attrib_list;
VALIDATE_DISPLAY_INIT(dpy, NULL);
VALIDATE_CONFIG(config, EGL_FALSE);
if (win == 0) {
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE);
}
EGLint surfaceType;
if (s_display.getConfigAttrib(config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) return EGL_FALSE;
if (!(surfaceType & EGL_WINDOW_BIT)) {
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE);
}
if (reinterpret_cast<ANativeWindow*>(win)->common.magic != ANDROID_NATIVE_WINDOW_MAGIC) {
setErrorReturn(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
}
egl_surface_t* surface = egl_window_surface_t::create(
&s_display, config, EGL_WINDOW_BIT, reinterpret_cast<ANativeWindow*>(win));
if (!surface) {
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE);
}
return surface;
}
EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list)
{
VALIDATE_DISPLAY_INIT(dpy, NULL);
VALIDATE_CONFIG(config, EGL_FALSE);
EGLint surfaceType;
if (s_display.getConfigAttrib(config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) return EGL_FALSE;
if (!(surfaceType & EGL_PBUFFER_BIT)) {
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE);
}
int32_t w = 0;
int32_t h = 0;
EGLint texFormat = EGL_NO_TEXTURE;
EGLint texTarget = EGL_NO_TEXTURE;
while (attrib_list[0] != EGL_NONE) {
switch (attrib_list[0]) {
case EGL_WIDTH:
w = attrib_list[1];
if (w < 0) setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
break;
case EGL_HEIGHT:
h = attrib_list[1];
if (h < 0) setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
break;
case EGL_TEXTURE_FORMAT:
texFormat = attrib_list[1];
break;
case EGL_TEXTURE_TARGET:
texTarget = attrib_list[1];
break;
// the followings are not supported
case EGL_LARGEST_PBUFFER:
case EGL_MIPMAP_TEXTURE:
case EGL_VG_ALPHA_FORMAT:
case EGL_VG_COLORSPACE:
break;
default:
ALOGE("%s:%d unknown attribute: 0x%x\n", __func__, __LINE__, attrib_list[0]);
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
};
attrib_list+=2;
}
if (((texFormat == EGL_NO_TEXTURE)&&(texTarget != EGL_NO_TEXTURE)) ||
((texFormat != EGL_NO_TEXTURE)&&(texTarget == EGL_NO_TEXTURE))) {
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE);
}
// TODO: check EGL_TEXTURE_FORMAT - need to support eglBindTexImage
GLenum pixelFormat;
if (s_display.getConfigGLPixelFormat(config, &pixelFormat) == EGL_FALSE)
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE);
egl_surface_t* surface = egl_pbuffer_surface_t::create(dpy, config,
EGL_PBUFFER_BIT, w, h, pixelFormat);
if (!surface) {
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE);
}
//setup attributes
surface->setTextureFormat(texFormat);
surface->setTextureTarget(texTarget);
return surface;
}
EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list)
{
//XXX: Pixmap not supported. The host cannot render to a pixmap resource
// located on host. In order to support Pixmaps we should either punt
// to s/w rendering -or- let the host render to a buffer that will be
// copied back to guest at some sync point. None of those methods not
// implemented and pixmaps are not used with OpenGL anyway ...
VALIDATE_CONFIG(config, EGL_FALSE);
(void)dpy;
(void)pixmap;
(void)attrib_list;
return EGL_NO_SURFACE;
}
EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_SURFACE_RETURN(eglSurface, EGL_FALSE);
egl_surface_t* surface(static_cast<egl_surface_t*>(eglSurface));
if (surface->isCurrent()) {
surface->deletePending = 1;
} else {
delete surface;
}
return EGL_TRUE;
}
static float s_getNativeDpi() {
float nativeDPI = 560.0f;
const char* dpiPropName = "qemu.sf.lcd_density";
char dpiProp[PROPERTY_VALUE_MAX];
if (property_get(dpiPropName, dpiProp, NULL) > 0) {
nativeDPI = atof(dpiProp);
}
return nativeDPI;
}
EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface eglSurface, EGLint attribute, EGLint *value)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_SURFACE_RETURN(eglSurface, EGL_FALSE);
egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) );
// Parameters involved in queries of EGL_(HORIZONTAL|VERTICAL)_RESOLUTION
float currWidth, currHeight, scaledResolution, effectiveSurfaceDPI;
EGLBoolean ret = EGL_TRUE;
switch (attribute) {
case EGL_CONFIG_ID:
ret = s_display.getConfigAttrib(surface->config, EGL_CONFIG_ID, value);
break;
case EGL_WIDTH:
*value = surface->getWidth();
break;
case EGL_HEIGHT:
*value = surface->getHeight();
break;
case EGL_TEXTURE_FORMAT:
if (surface->getSurfaceType() & EGL_PBUFFER_BIT) {
*value = surface->getTextureFormat();
}
break;
case EGL_TEXTURE_TARGET:
if (surface->getSurfaceType() & EGL_PBUFFER_BIT) {
*value = surface->getTextureTarget();
}
break;
case EGL_SWAP_BEHAVIOR:
{
EGLint surfaceType;
ret = s_display.getConfigAttrib(surface->config, EGL_SURFACE_TYPE,
&surfaceType);
if (ret == EGL_TRUE) {
if (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) {
*value = EGL_BUFFER_PRESERVED;
} else {
*value = EGL_BUFFER_DESTROYED;
}
}
break;
}
case EGL_LARGEST_PBUFFER:
// not modified for a window or pixmap surface
// and we ignore it when creating a PBuffer surface (default is EGL_FALSE)
if (surface->getSurfaceType() & EGL_PBUFFER_BIT) *value = EGL_FALSE;
break;
case EGL_MIPMAP_TEXTURE:
// not modified for a window or pixmap surface
// and we ignore it when creating a PBuffer surface (default is 0)
if (surface->getSurfaceType() & EGL_PBUFFER_BIT) *value = false;
break;
case EGL_MIPMAP_LEVEL:
// not modified for a window or pixmap surface
// and we ignore it when creating a PBuffer surface (default is 0)
if (surface->getSurfaceType() & EGL_PBUFFER_BIT) *value = 0;
break;
case EGL_MULTISAMPLE_RESOLVE:
// ignored when creating the surface, return default
*value = EGL_MULTISAMPLE_RESOLVE_DEFAULT;
break;
case EGL_HORIZONTAL_RESOLUTION:
// pixel/mm * EGL_DISPLAY_SCALING
// TODO: get the DPI from avd config
currWidth = surface->getWidth();
scaledResolution = currWidth / surface->getNativeWidth();
effectiveSurfaceDPI =
scaledResolution * s_getNativeDpi() * EGL_DISPLAY_SCALING;
*value = (EGLint)(effectiveSurfaceDPI);
break;
case EGL_VERTICAL_RESOLUTION:
// pixel/mm * EGL_DISPLAY_SCALING
// TODO: get the real DPI from avd config
currHeight = surface->getHeight();
scaledResolution = currHeight / surface->getNativeHeight();
effectiveSurfaceDPI =
scaledResolution * s_getNativeDpi() * EGL_DISPLAY_SCALING;
*value = (EGLint)(effectiveSurfaceDPI);
break;
case EGL_PIXEL_ASPECT_RATIO:
// w / h * EGL_DISPLAY_SCALING
// Please don't ask why * EGL_DISPLAY_SCALING, the document says it
*value = 1 * EGL_DISPLAY_SCALING;
break;
case EGL_RENDER_BUFFER:
switch (surface->getSurfaceType()) {
case EGL_PBUFFER_BIT:
*value = EGL_BACK_BUFFER;
break;
case EGL_PIXMAP_BIT:
*value = EGL_SINGLE_BUFFER;
break;
case EGL_WINDOW_BIT:
// ignored when creating the surface, return default
*value = EGL_BACK_BUFFER;
break;
default:
ALOGE("eglQuerySurface %x unknown surface type %x",
attribute, surface->getSurfaceType());
ret = setErrorFunc(EGL_BAD_ATTRIBUTE, EGL_FALSE);
break;
}
break;
case EGL_VG_COLORSPACE:
// ignored when creating the surface, return default
*value = EGL_VG_COLORSPACE_sRGB;
break;
case EGL_VG_ALPHA_FORMAT:
// ignored when creating the surface, return default
*value = EGL_VG_ALPHA_FORMAT_NONPRE;
break;
case EGL_TIMESTAMPS_ANDROID:
*value = surface->isCollectingTimestamps();
break;
//TODO: complete other attributes
default:
ALOGE("eglQuerySurface %x EGL_BAD_ATTRIBUTE", attribute);
ret = setErrorFunc(EGL_BAD_ATTRIBUTE, EGL_FALSE);
break;
}
return ret;
}
EGLBoolean eglBindAPI(EGLenum api)
{
if (api != EGL_OPENGL_ES_API)
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
return EGL_TRUE;
}
EGLenum eglQueryAPI()
{
return EGL_OPENGL_ES_API;
}
EGLBoolean eglWaitClient()
{
return eglWaitGL();
}
// We may need to trigger this directly from the TLS destructor.
static EGLBoolean s_eglReleaseThreadImpl(EGLThreadInfo* tInfo) {
if (!tInfo) return EGL_TRUE;
tInfo->eglError = EGL_SUCCESS;
EGLContext_t* context = tInfo->currentContext;
if (!context || !s_display.isContext(context)) {
HostConnection::exit();
return EGL_TRUE;
}
// The following code is doing pretty much the same thing as
// eglMakeCurrent(&s_display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE)
// with the only issue that we do not require a valid display here.
DEFINE_AND_VALIDATE_HOST_CONNECTION_FOR_TLS(EGL_FALSE, tInfo);
// We are going to call makeCurrent on the null context and surface
// anyway once we are on the host, so skip rcMakeCurrent here.
// rcEnc->rcMakeCurrent(rcEnc, 0, 0, 0);
context->flags &= ~EGLContext_t::IS_CURRENT;
s_destroyPendingSurfacesInContext(context);
if (context->deletePending) {
if (context->rcContext) {
rcEnc->rcDestroyContext(rcEnc, context->rcContext);
context->rcContext = 0;
}
delete context;
}
tInfo->currentContext = 0;
HostConnection::exit();
return EGL_TRUE;
}
EGLBoolean eglReleaseThread()
{
return s_eglReleaseThreadImpl(getEGLThreadInfo());
}
EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list)
{
//TODO
(void)dpy;
(void)buftype;
(void)buffer;
(void)config;
(void)attrib_list;
ALOGW("%s not implemented", __FUNCTION__);
return 0;
}
EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
{
// Right now we don't do anything when using host GPU.
// This is purely just to pass the data through
// without issuing a warning. We may benefit from validating the
// display and surface for debug purposes.
// TODO: Find cases where we actually need to do something.
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_SURFACE_RETURN(surface, EGL_FALSE);
if (surface == EGL_NO_SURFACE) {
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
}
(void)value;
egl_surface_t* p_surface( static_cast<egl_surface_t*>(surface) );
switch (attribute) {
case EGL_MIPMAP_LEVEL:
return true;
case EGL_MULTISAMPLE_RESOLVE:
{
if (value == EGL_MULTISAMPLE_RESOLVE_BOX) {
EGLint surface_type;
s_display.getConfigAttrib(p_surface->config, EGL_SURFACE_TYPE, &surface_type);
if (0 == (surface_type & EGL_MULTISAMPLE_RESOLVE_BOX_BIT)) {
setErrorReturn(EGL_BAD_MATCH, EGL_FALSE);
}
}
return true;
}
case EGL_SWAP_BEHAVIOR:
if (value == EGL_BUFFER_PRESERVED) {
EGLint surface_type;
s_display.getConfigAttrib(p_surface->config, EGL_SURFACE_TYPE, &surface_type);
if (0 == (surface_type & EGL_SWAP_BEHAVIOR_PRESERVED_BIT)) {
setErrorReturn(EGL_BAD_MATCH, EGL_FALSE);
}
}
return true;
case EGL_TIMESTAMPS_ANDROID:
DPRINT("%s: set frame timestamps collecting %d\n", __func__, value);
p_surface->setCollectingTimestamps(value);
return true;
default:
ALOGW("%s: attr=0x%x not implemented", __FUNCTION__, attribute);
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE);
}
return false;
}
EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface eglSurface, EGLint buffer)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_SURFACE_RETURN(eglSurface, EGL_FALSE);
if (eglSurface == EGL_NO_SURFACE) {
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
}
if (buffer != EGL_BACK_BUFFER) {
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) );
if (surface->getTextureFormat() == EGL_NO_TEXTURE) {
setErrorReturn(EGL_BAD_MATCH, EGL_FALSE);
}
if (!(surface->getSurfaceType() & EGL_PBUFFER_BIT)) {
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
}
//It's now safe to cast to pbuffer surface
egl_pbuffer_surface_t* pbSurface = (egl_pbuffer_surface_t*)surface;
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
rcEnc->rcBindTexture(rcEnc, pbSurface->getRcColorBuffer());
return GL_TRUE;
}
EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
{
//TODO
(void)dpy;
(void)surface;
(void)buffer;
ALOGW("%s not implemented", __FUNCTION__);
return 0;
}
EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
EGLContext_t* ctx = getEGLThreadInfo()->currentContext;
if (!ctx) {
setErrorReturn(EGL_BAD_CONTEXT, EGL_FALSE);
}
if (!ctx->draw) {
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
}
egl_surface_t* draw(static_cast<egl_surface_t*>(ctx->draw));
draw->setSwapInterval(interval);
rcEnc->rcFBSetSwapInterval(rcEnc, interval); //TODO: implement on the host
return EGL_TRUE;
}
EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_NO_CONTEXT);
VALIDATE_CONFIG(config, EGL_NO_CONTEXT);
EGLint majorVersion = 1; //default
EGLint minorVersion = 0;
EGLint context_flags = 0;
EGLint profile_mask = 0;
bool wantedMajorVersion = false;
bool wantedMinorVersion = false;
while (attrib_list && attrib_list[0] != EGL_NONE) {
EGLint attrib_val = attrib_list[1];
switch(attrib_list[0]) {
case EGL_CONTEXT_MAJOR_VERSION_KHR:
majorVersion = attrib_val;
wantedMajorVersion = true;
break;
case EGL_CONTEXT_MINOR_VERSION_KHR:
minorVersion = attrib_val;
wantedMinorVersion = true;
break;
case EGL_CONTEXT_FLAGS_KHR:
if ((attrib_val & EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR) ||
(attrib_val & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) ||
(attrib_val & EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR)) {
context_flags = attrib_val;
} else {
RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE);
}
break;
case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR:
if ((attrib_val | EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR) ||
(attrib_val | EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR)) {
profile_mask = attrib_val;
} else {
RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE);
}
break;
case EGL_CONTEXT_PRIORITY_LEVEL_IMG:
// According to the spec, we are allowed not to honor this hint.
// https://www.khronos.org/registry/EGL/extensions/IMG/EGL_IMG_context_priority.txt
break;
default:
ALOGV("eglCreateContext unsupported attrib 0x%x", attrib_list[0]);
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
}
attrib_list+=2;
}
// Support up to GLES 3.2 depending on advertised version from the host system.
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_NO_CONTEXT);
if (rcEnc->getGLESMaxVersion() >= GLES_MAX_VERSION_3_0) {
if (!wantedMajorVersion) {
majorVersion = 1;
wantedMinorVersion = false;
}
if (wantedMajorVersion &&
majorVersion == 2) {
majorVersion = 3;
wantedMinorVersion = false;
}
if (majorVersion == 3 && !wantedMinorVersion) {
switch (rcEnc->getGLESMaxVersion()) {
case GLES_MAX_VERSION_3_0:
minorVersion = 0;
break;
case GLES_MAX_VERSION_3_1:
minorVersion = 1;
break;
case GLES_MAX_VERSION_3_2:
minorVersion = 2;
break;
default:
minorVersion = 0;
break;
}
}
} else {
if (!wantedMajorVersion) {
majorVersion = 1;
}
}
switch (majorVersion) {
case 1:
case 2:
break;
case 3:
if (rcEnc->getGLESMaxVersion() < GLES_MAX_VERSION_3_0) {
ALOGE("%s: EGL_BAD_CONFIG: no ES 3 support", __FUNCTION__);
setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
}
switch (minorVersion) {
case 0:
break;
case 1:
if (rcEnc->getGLESMaxVersion() < GLES_MAX_VERSION_3_1) {
ALOGE("%s: EGL_BAD_CONFIG: no ES 3.1 support", __FUNCTION__);
setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
}
break;
case 2:
if (rcEnc->getGLESMaxVersion() < GLES_MAX_VERSION_3_2) {
ALOGE("%s: EGL_BAD_CONFIG: no ES 3.2 support", __FUNCTION__);
setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
}
break;
default:
ALOGE("%s: EGL_BAD_CONFIG: Unknown ES version %d.%d",
__FUNCTION__, majorVersion, minorVersion);
setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
}
break;
default:
ALOGE("%s:%d EGL_BAD_CONFIG: invalid major GLES version: %d\n",
__func__, __LINE__, majorVersion);
setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
}
uint32_t rcShareCtx = 0;
EGLContext_t * shareCtx = NULL;
if (share_context) {
shareCtx = static_cast<EGLContext_t*>(share_context);
rcShareCtx = shareCtx->rcContext;
if (shareCtx->dpy != dpy)
setErrorReturn(EGL_BAD_MATCH, EGL_NO_CONTEXT);
}
// We've created EGL context. Disconnecting
// would be dangerous at this point.
hostCon->setGrallocOnly(false);
int rcMajorVersion = majorVersion;
if (majorVersion == 3 && minorVersion == 1) {
rcMajorVersion = 4;
}
if (majorVersion == 3 && minorVersion == 2) {
rcMajorVersion = 4;
}
uint32_t rcContext = rcEnc->rcCreateContext(rcEnc, (uintptr_t)s_display.getIndexOfConfig(config), rcShareCtx, rcMajorVersion);
if (!rcContext) {
ALOGE("rcCreateContext returned 0");
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_CONTEXT);
}
EGLContext_t * context = new EGLContext_t(dpy, config, shareCtx, majorVersion, minorVersion);
DPRINT("%s: %p: maj %d min %d rcv %d", __FUNCTION__, context, majorVersion, minorVersion, rcMajorVersion);
if (!context) {
ALOGE("could not alloc egl context!");
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_CONTEXT);
}
context->rcContext = rcContext;
return context;
}
EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_CONTEXT_RETURN(ctx, EGL_FALSE);
EGLContext_t * context = static_cast<EGLContext_t*>(ctx);
if (context->flags & EGLContext_t::IS_CURRENT) {
context->deletePending = 1;
return EGL_TRUE;
}
if (context->rcContext) {
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
rcEnc->rcDestroyContext(rcEnc, context->rcContext);
context->rcContext = 0;
}
delete context;
return EGL_TRUE;
}
EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_SURFACE_RETURN(draw, EGL_FALSE);
VALIDATE_SURFACE_RETURN(read, EGL_FALSE);
// Only place to initialize the TLS destructor; any
// thread can suddenly jump in any eglMakeCurrent
setTlsDestructor((tlsDtorCallback)s_eglReleaseThreadImpl);
if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT))
setErrorReturn(EGL_BAD_MATCH, EGL_FALSE);
if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT))
setErrorReturn(EGL_BAD_MATCH, EGL_FALSE);
EGLContext_t * context = static_cast<EGLContext_t*>(ctx);
uint32_t ctxHandle = (context) ? context->rcContext : 0;
egl_surface_t * drawSurf = static_cast<egl_surface_t *>(draw);
uint32_t drawHandle = (drawSurf) ? drawSurf->getRcSurface() : 0;
egl_surface_t * readSurf = static_cast<egl_surface_t *>(read);
uint32_t readHandle = (readSurf) ? readSurf->getRcSurface() : 0;
//
// Nothing to do if no binding change has made
//
EGLThreadInfo *tInfo = getEGLThreadInfo();
if (tInfo->currentContext == context &&
(context == NULL ||
(context && (context->draw == draw) && (context->read == read)))) {
return EGL_TRUE;
}
// Destroy surfaces while the previous context is still current.
EGLContext_t* prevCtx = tInfo->currentContext;
if (tInfo->currentContext) {
if (prevCtx->draw) {
static_cast<egl_surface_t *>(prevCtx->draw)->setIsCurrent(false);
}
if (prevCtx->read) {
static_cast<egl_surface_t *>(prevCtx->read)->setIsCurrent(false);
}
s_destroyPendingSurfacesInContext(tInfo->currentContext);
}
if (context && (context->flags & EGLContext_t::IS_CURRENT) && (context != tInfo->currentContext)) {
// context is current to another thread
ALOGE("%s: error: EGL_BAD_ACCESS: context %p current to another thread!\n", __FUNCTION__, context);
setErrorReturn(EGL_BAD_ACCESS, EGL_FALSE);
}
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
if (rcEnc->hasAsyncFrameCommands()) {
rcEnc->rcMakeCurrentAsync(rcEnc, ctxHandle, drawHandle, readHandle);
} else {
rcEnc->rcMakeCurrent(rcEnc, ctxHandle, drawHandle, readHandle);
}
//Now make the local bind
if (context) {
// This is a nontrivial context.
// The thread cannot be gralloc-only anymore.
hostCon->setGrallocOnly(false);
context->draw = draw;
context->read = read;
if (drawSurf) {
drawSurf->setIsCurrent(true);
}
if (readSurf) {
readSurf->setIsCurrent(true);
}
context->flags |= EGLContext_t::IS_CURRENT;
GLClientState* contextState =
context->getClientState();
if (!hostCon->gl2Encoder()->isInitialized()) {
DPRINT("%s: %p: ver %d %d (tinfo %p) (first time)",
__FUNCTION__,
context, context->majorVersion, context->minorVersion, tInfo);
s_display.gles2_iface()->init();
hostCon->gl2Encoder()->setInitialized();
ClientAPIExts::initClientFuncs(s_display.gles2_iface(), 1);
}
if (contextState->needsInitFromCaps()) {
// Need to set the version first if
// querying caps, or validation will trip incorrectly.
hostCon->gl2Encoder()->setVersion(
context->majorVersion,
context->minorVersion,
context->deviceMajorVersion,
context->deviceMinorVersion);
hostCon->gl2Encoder()->setClientState(contextState);
if (context->majorVersion > 1) {
HostDriverCaps caps = s_display.getHostDriverCaps(
context->majorVersion,
context->minorVersion);
contextState->initFromCaps(caps);
} else {
// Just put some stuff here to make gles1 happy
HostDriverCaps gles1Caps = {
.max_vertex_attribs = 16,
.max_combined_texture_image_units = 8,
.max_color_attachments = 8,
.max_texture_size = 4096,
.max_texture_size_cube_map = 2048,
.max_renderbuffer_size = 4096,
};
contextState->initFromCaps(gles1Caps);
}
}
// update the client state, share group, and version
if (context->majorVersion > 1) {
hostCon->gl2Encoder()->setClientStateMakeCurrent(
contextState,
context->majorVersion,
context->minorVersion,
context->deviceMajorVersion,
context->deviceMinorVersion);
hostCon->gl2Encoder()->setSharedGroup(context->getSharedGroup());
}
else {
hostCon->glEncoder()->setClientState(context->getClientState());
hostCon->glEncoder()->setSharedGroup(context->getSharedGroup());
}
}
else if (tInfo->currentContext) {
//release ClientState & SharedGroup
if (tInfo->currentContext->majorVersion > 1) {
hostCon->gl2Encoder()->setClientState(NULL);
hostCon->gl2Encoder()->setSharedGroup(GLSharedGroupPtr(NULL));
}
else {
hostCon->glEncoder()->setClientState(NULL);
hostCon->glEncoder()->setSharedGroup(GLSharedGroupPtr(NULL));
}
}
// Delete the previous context here
if (tInfo->currentContext && (tInfo->currentContext != context)) {
tInfo->currentContext->flags &= ~EGLContext_t::IS_CURRENT;
if (tInfo->currentContext->deletePending && tInfo->currentContext != context) {
eglDestroyContext(dpy, tInfo->currentContext);
}
}
// Now the new context is current in tInfo
tInfo->currentContext = context;
//Check maybe we need to init the encoder, if it's first eglMakeCurrent
if (tInfo->currentContext) {
if (tInfo->currentContext->majorVersion > 1) {
if (!hostCon->gl2Encoder()->isInitialized()) {
s_display.gles2_iface()->init();
hostCon->gl2Encoder()->setInitialized();
ClientAPIExts::initClientFuncs(s_display.gles2_iface(), 1);
}
const char* exts = getGLString(GL_EXTENSIONS);
if (exts) {
hostCon->gl2Encoder()->setExtensions(exts, getExtStringArray());
}
}
else {
if (!hostCon->glEncoder()->isInitialized()) {
DPRINT("%s: %p: ver %d %d (tinfo %p) (first time)",
__FUNCTION__,
context, context->majorVersion, context->minorVersion, tInfo);
s_display.gles_iface()->init();
hostCon->glEncoder()->setInitialized();
ClientAPIExts::initClientFuncs(s_display.gles_iface(), 0);
}
}
}
return EGL_TRUE;
}
EGLContext eglGetCurrentContext()
{
return getEGLThreadInfo()->currentContext;
}
EGLSurface eglGetCurrentSurface(EGLint readdraw)
{
EGLContext_t * context = getEGLThreadInfo()->currentContext;
if (!context)
return EGL_NO_SURFACE; //not an error
switch (readdraw) {
case EGL_READ:
return context->read;
case EGL_DRAW:
return context->draw;
default:
ALOGE("%s:%d unknown parameter: 0x%x\n", __func__, __LINE__, readdraw);
setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
}
EGLDisplay eglGetCurrentDisplay()
{
EGLContext_t * context = getEGLThreadInfo()->currentContext;
if (!context)
return EGL_NO_DISPLAY; //not an error
return context->dpy;
}
EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
VALIDATE_CONTEXT_RETURN(ctx, EGL_FALSE);
EGLContext_t * context = static_cast<EGLContext_t*>(ctx);
EGLBoolean ret = EGL_TRUE;
switch (attribute) {
case EGL_CONFIG_ID:
ret = s_display.getConfigAttrib(context->config, EGL_CONFIG_ID, value);
break;
case EGL_CONTEXT_CLIENT_TYPE:
*value = EGL_OPENGL_ES_API;
break;
case EGL_CONTEXT_CLIENT_VERSION:
*value = context->majorVersion;
break;
case EGL_RENDER_BUFFER:
if (!context->draw)
*value = EGL_NONE;
else
*value = EGL_BACK_BUFFER; //single buffer not supported
break;
default:
ALOGE("eglQueryContext %x EGL_BAD_ATTRIBUTE", attribute);
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE);
break;
}
return ret;
}
EGLBoolean eglWaitGL()
{
EGLThreadInfo *tInfo = getEGLThreadInfo();
if (!tInfo || !tInfo->currentContext) {
return EGL_FALSE;
}
if (tInfo->currentContext->majorVersion > 1) {
s_display.gles2_iface()->finish();
}
else {
s_display.gles_iface()->finish();
}
return EGL_TRUE;
}
EGLBoolean eglWaitNative(EGLint engine)
{
(void)engine;
return EGL_TRUE;
}
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface eglSurface)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
if (eglSurface == EGL_NO_SURFACE)
setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
egl_surface_t* d = static_cast<egl_surface_t*>(eglSurface);
if (d->dpy != dpy)
setErrorReturn(EGL_BAD_DISPLAY, EGL_FALSE);
// post the surface
EGLBoolean ret = d->swapBuffers();
hostCon->flush();
return ret;
}
EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target)
{
//TODO :later
(void)dpy;
(void)surface;
(void)target;
return 0;
}
EGLBoolean eglLockSurfaceKHR(EGLDisplay display, EGLSurface surface, const EGLint *attrib_list)
{
//TODO later
(void)display;
(void)surface;
(void)attrib_list;
return 0;
}
EGLBoolean eglUnlockSurfaceKHR(EGLDisplay display, EGLSurface surface)
{
//TODO later
(void)display;
(void)surface;
return 0;
}
EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list)
{
(void)attrib_list;
VALIDATE_DISPLAY_INIT(dpy, EGL_NO_IMAGE_KHR);
if (target == EGL_NATIVE_BUFFER_ANDROID) {
if (ctx != EGL_NO_CONTEXT) {
setErrorReturn(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
}
android_native_buffer_t* native_buffer = (android_native_buffer_t*)buffer;
if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
if (native_buffer->common.version != sizeof(android_native_buffer_t))
setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
int format = grallocHelper->getFormat(native_buffer->handle);
switch (format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_YV12:
case HAL_PIXEL_FORMAT_BGRA_8888:
#if PLATFORM_SDK_VERSION >= 26
case HAL_PIXEL_FORMAT_RGBA_FP16:
case HAL_PIXEL_FORMAT_RGBA_1010102:
#endif
#if PLATFORM_SDK_VERSION >= 28
case HAL_PIXEL_FORMAT_YCBCR_420_888:
#endif
#if PLATFORM_SDK_VERSION >= 30
case HAL_PIXEL_FORMAT_YCBCR_P010:
#endif
break;
case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
ALOGW("%s:%d using HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED\n", __func__, __LINE__);
break;
default:
ALOGE("%s:%d unknown parameter: 0x%x\n", __func__, __LINE__, format);
setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
}
native_buffer->common.incRef(&native_buffer->common);
EGLImage_t *image = new EGLImage_t();
image->dpy = dpy;
image->target = target;
image->native_buffer = native_buffer;
image->width = native_buffer->width;
image->height = native_buffer->width;
return (EGLImageKHR)image;
}
else if (target == EGL_GL_TEXTURE_2D_KHR) {
VALIDATE_CONTEXT_RETURN(ctx, EGL_NO_IMAGE_KHR);
EGLContext_t *context = static_cast<EGLContext_t*>(ctx);
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_NO_IMAGE_KHR);
uint32_t ctxHandle = (context) ? context->rcContext : 0;
GLuint texture = (GLuint)reinterpret_cast<uintptr_t>(buffer);
uint32_t img = rcEnc->rcCreateClientImage(rcEnc, ctxHandle, target, texture);
EGLImage_t *image = new EGLImage_t();
image->dpy = dpy;
image->target = target;
image->host_egl_image = img;
image->width = context->getClientState()->queryTexWidth(0, texture);
image->height = context->getClientState()->queryTexHeight(0, texture);
return (EGLImageKHR)image;
}
setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
}
EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
EGLImage_t *image = (EGLImage_t*)img;
if (!image || image->dpy != dpy) {
RETURN_ERROR(EGL_FALSE, EGL_BAD_PARAMETER);
}
if (image->target == EGL_NATIVE_BUFFER_ANDROID) {
android_native_buffer_t* native_buffer = image->native_buffer;
if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
if (native_buffer->common.version != sizeof(android_native_buffer_t))
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
native_buffer->common.decRef(&native_buffer->common);
delete image;
return EGL_TRUE;
}
else if (image->target == EGL_GL_TEXTURE_2D_KHR) {
uint32_t host_egl_image = image->host_egl_image;
delete image;
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
return rcEnc->rcDestroyClientImage(rcEnc, host_egl_image);
}
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
#define FENCE_SYNC_HANDLE (EGLSyncKHR)0xFE4CE
#define MAX_EGL_SYNC_ATTRIBS 10
EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type,
const EGLint *attrib_list)
{
VALIDATE_DISPLAY(dpy, EGL_NO_SYNC_KHR);
DPRINT("type for eglCreateSyncKHR: 0x%x", type);
DEFINE_HOST_CONNECTION;
if ((type != EGL_SYNC_FENCE_KHR &&
type != EGL_SYNC_NATIVE_FENCE_ANDROID) ||
(type != EGL_SYNC_FENCE_KHR &&
!rcEnc->hasNativeSync() &&
!rcEnc->hasVirtioGpuNativeSync())) {
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR);
}
EGLThreadInfo *tInfo = getEGLThreadInfo();
if (!tInfo || !tInfo->currentContext) {
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SYNC_KHR);
}
int num_actual_attribs = 0;
// If attrib_list is not NULL,
// ensure attrib_list contains (key, value) pairs
// followed by a single EGL_NONE.
// Also validate attribs.
int inputFenceFd = -1;
if (attrib_list) {
for (int i = 0; i < MAX_EGL_SYNC_ATTRIBS; i += 2) {
if (attrib_list[i] == EGL_NONE) {
num_actual_attribs = i;
break;
}
if (i + 1 == MAX_EGL_SYNC_ATTRIBS) {
DPRINT("ERROR: attrib list without EGL_NONE");
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR);
}
}
// Validate and input attribs
for (int i = 0; i < num_actual_attribs; i += 2) {
EGLint attrib_key = attrib_list[i];
EGLint attrib_val = attrib_list[i + 1];
switch (attrib_key) {
case EGL_SYNC_TYPE_KHR:
case EGL_SYNC_STATUS_KHR:
case EGL_SYNC_CONDITION_KHR:
case EGL_SYNC_NATIVE_FENCE_FD_ANDROID:
break;
default:
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR);
}
if (attrib_key == EGL_SYNC_NATIVE_FENCE_FD_ANDROID) {
if (attrib_val != EGL_NO_NATIVE_FENCE_FD_ANDROID) {
inputFenceFd = attrib_val;
}
}
DPRINT("attrib: 0x%x : 0x%x", attrib_key, attrib_val);
}
}
uint64_t sync_handle = 0;
int newFenceFd = -1;
if (rcEnc->hasVirtioGpuNativeSync()) {
sync_handle =
createNativeSync_virtioGpu(
type, attrib_list, num_actual_attribs,
false /* don't destroy when signaled on the host;
let the guest clean this up,
because the guest called eglCreateSyncKHR. */,
inputFenceFd, &newFenceFd);
} else if (rcEnc->hasNativeSync()) {
sync_handle =
createNativeSync(
type, attrib_list, num_actual_attribs,
false /* don't destroy when signaled on the host;
let the guest clean this up,
because the guest called eglCreateSyncKHR. */,
inputFenceFd,
&newFenceFd);
} else {
// Just trigger a glFinish if the native sync on host
// is unavailable.
eglWaitClient();
}
EGLSync_t* syncRes = new EGLSync_t(sync_handle);
if (type == EGL_SYNC_NATIVE_FENCE_ANDROID) {
syncRes->type = EGL_SYNC_NATIVE_FENCE_ANDROID;
if (rcEnc->hasVirtioGpuNativeSync()) {
syncRes->android_native_fence_fd = newFenceFd;
} else {
if (inputFenceFd < 0) {
syncRes->android_native_fence_fd = newFenceFd;
} else {
DPRINT("has input fence fd %d",
inputFenceFd);
syncRes->android_native_fence_fd = inputFenceFd;
}
}
} else {
syncRes->type = EGL_SYNC_FENCE_KHR;
syncRes->android_native_fence_fd = -1;
if (!rcEnc->hasNativeSync() && !rcEnc->hasVirtioGpuNativeSync()) {
syncRes->status = EGL_SIGNALED_KHR;
}
}
return (EGLSyncKHR)syncRes;
}
EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR eglsync)
{
(void)dpy;
if (!eglsync) {
ALOGE("%s: null sync object!", __FUNCTION__);
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
EGLSync_t* sync = static_cast<EGLSync_t*>(eglsync);
if (sync && sync->android_native_fence_fd > 0) {
close(sync->android_native_fence_fd);
sync->android_native_fence_fd = -1;
}
if (sync) {
DEFINE_HOST_CONNECTION;
if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSync()) {
if (rcEnc->hasAsyncFrameCommands()) {
rcEnc->rcDestroySyncKHRAsync(rcEnc, sync->handle);
} else {
rcEnc->rcDestroySyncKHR(rcEnc, sync->handle);
}
}
delete sync;
}
return EGL_TRUE;
}
EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR eglsync, EGLint flags,
EGLTimeKHR timeout)
{
(void)dpy;
if (!eglsync) {
ALOGE("%s: null sync object!", __FUNCTION__);
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
EGLSync_t* sync = (EGLSync_t*)eglsync;
DPRINT("sync=0x%lx (handle=0x%lx) flags=0x%x timeout=0x%llx",
sync, sync->handle, flags, timeout);
DEFINE_HOST_CONNECTION;
EGLint retval;
if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSync()) {
retval = rcEnc->rcClientWaitSyncKHR
(rcEnc, sync->handle, flags, timeout);
} else {
retval = EGL_CONDITION_SATISFIED_KHR;
}
EGLint res_status;
switch (sync->type) {
case EGL_SYNC_FENCE_KHR:
res_status = EGL_SIGNALED_KHR;
break;
case EGL_SYNC_NATIVE_FENCE_ANDROID:
res_status = EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID;
break;
default:
res_status = EGL_SIGNALED_KHR;
}
sync->status = res_status;
return retval;
}
EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR eglsync,
EGLint attribute, EGLint *value)
{
(void)dpy;
EGLSync_t* sync = (EGLSync_t*)eglsync;
if (!sync) {
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
if (!value) {
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
switch (attribute) {
case EGL_SYNC_TYPE_KHR:
*value = sync->type;
return EGL_TRUE;
case EGL_SYNC_STATUS_KHR: {
if (sync->status == EGL_SIGNALED_KHR) {
*value = sync->status;
return EGL_TRUE;
} else {
// ask the host again
DEFINE_HOST_CONNECTION;
if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSyncV4()) {
if (rcEnc->rcIsSyncSignaled(rcEnc, sync->handle)) {
sync->status = EGL_SIGNALED_KHR;
}
}
*value = sync->status;
return EGL_TRUE;
}
}
case EGL_SYNC_CONDITION_KHR:
*value = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR;
return EGL_TRUE;
default:
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE);
}
}
int eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR eglsync) {
(void)dpy;
DPRINT("call");
EGLSync_t* sync = (EGLSync_t*)eglsync;
if (sync && sync->android_native_fence_fd > 0) {
int res = dup(sync->android_native_fence_fd);
return res;
} else {
return -1;
}
}
EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR eglsync, EGLint flags) {
(void)dpy;
if (!eglsync) {
ALOGE("%s: null sync object!", __FUNCTION__);
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
if (flags) {
ALOGE("%s: flags must be 0, got 0x%x", __FUNCTION__, flags);
setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
DEFINE_HOST_CONNECTION;
if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSyncV3()) {
EGLSync_t* sync = (EGLSync_t*)eglsync;
rcEnc->rcWaitSyncKHR(rcEnc, sync->handle, flags);
}
return EGL_TRUE;
}