| /* |
| * 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; |
| } |