1/*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "ScreenRecord"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#define EGL_EGLEXT_PROTOTYPES
22
23#include <gui/BufferQueue.h>
24#include <gui/GraphicBufferAlloc.h>
25#include <gui/Surface.h>
26
27#include "EglWindow.h"
28
29#include <EGL/egl.h>
30#include <EGL/eglext.h>
31
32#include <assert.h>
33
34using namespace android;
35
36
37status_t EglWindow::createWindow(const sp<IGraphicBufferProducer>& surface) {
38    if (mEglSurface != EGL_NO_SURFACE) {
39        ALOGE("surface already created");
40        return UNKNOWN_ERROR;
41    }
42    status_t err = eglSetupContext(false);
43    if (err != NO_ERROR) {
44        return err;
45    }
46
47    // Cache the current dimensions.  We're not expecting these to change.
48    surface->query(NATIVE_WINDOW_WIDTH, &mWidth);
49    surface->query(NATIVE_WINDOW_HEIGHT, &mHeight);
50
51    // Output side (EGL surface to draw on).
52    sp<ANativeWindow> anw = new Surface(surface);
53    mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, anw.get(),
54            NULL);
55    if (mEglSurface == EGL_NO_SURFACE) {
56        ALOGE("eglCreateWindowSurface error: %#x", eglGetError());
57        eglRelease();
58        return UNKNOWN_ERROR;
59    }
60
61    return NO_ERROR;
62}
63
64status_t EglWindow::createPbuffer(int width, int height) {
65    if (mEglSurface != EGL_NO_SURFACE) {
66        ALOGE("surface already created");
67        return UNKNOWN_ERROR;
68    }
69    status_t err = eglSetupContext(true);
70    if (err != NO_ERROR) {
71        return err;
72    }
73
74    mWidth = width;
75    mHeight = height;
76
77    EGLint pbufferAttribs[] = {
78            EGL_WIDTH, width,
79            EGL_HEIGHT, height,
80            EGL_NONE
81    };
82    mEglSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, pbufferAttribs);
83    if (mEglSurface == EGL_NO_SURFACE) {
84        ALOGE("eglCreatePbufferSurface error: %#x", eglGetError());
85        eglRelease();
86        return UNKNOWN_ERROR;
87    }
88
89    return NO_ERROR;
90}
91
92status_t EglWindow::makeCurrent() const {
93    if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
94        ALOGE("eglMakeCurrent failed: %#x", eglGetError());
95        return UNKNOWN_ERROR;
96    }
97    return NO_ERROR;
98}
99
100status_t EglWindow::eglSetupContext(bool forPbuffer) {
101    EGLBoolean result;
102
103    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
104    if (mEglDisplay == EGL_NO_DISPLAY) {
105        ALOGE("eglGetDisplay failed: %#x", eglGetError());
106        return UNKNOWN_ERROR;
107    }
108
109    EGLint majorVersion, minorVersion;
110    result = eglInitialize(mEglDisplay, &majorVersion, &minorVersion);
111    if (result != EGL_TRUE) {
112        ALOGE("eglInitialize failed: %#x", eglGetError());
113        return UNKNOWN_ERROR;
114    }
115    ALOGV("Initialized EGL v%d.%d", majorVersion, minorVersion);
116
117    EGLint numConfigs = 0;
118    EGLint windowConfigAttribs[] = {
119            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
120            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
121            EGL_RECORDABLE_ANDROID, 1,
122            EGL_RED_SIZE, 8,
123            EGL_GREEN_SIZE, 8,
124            EGL_BLUE_SIZE, 8,
125            // no alpha
126            EGL_NONE
127    };
128    EGLint pbufferConfigAttribs[] = {
129            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
130            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
131            EGL_RED_SIZE, 8,
132            EGL_GREEN_SIZE, 8,
133            EGL_BLUE_SIZE, 8,
134            EGL_ALPHA_SIZE, 8,
135            EGL_NONE
136    };
137    result = eglChooseConfig(mEglDisplay,
138            forPbuffer ? pbufferConfigAttribs : windowConfigAttribs,
139            &mEglConfig, 1, &numConfigs);
140    if (result != EGL_TRUE) {
141        ALOGE("eglChooseConfig error: %#x", eglGetError());
142        return UNKNOWN_ERROR;
143    }
144
145    EGLint contextAttribs[] = {
146        EGL_CONTEXT_CLIENT_VERSION, 2,
147        EGL_NONE
148    };
149    mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
150            contextAttribs);
151    if (mEglContext == EGL_NO_CONTEXT) {
152        ALOGE("eglCreateContext error: %#x", eglGetError());
153        return UNKNOWN_ERROR;
154    }
155
156    return NO_ERROR;
157}
158
159void EglWindow::eglRelease() {
160    ALOGV("EglWindow::eglRelease");
161    if (mEglDisplay != EGL_NO_DISPLAY) {
162        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
163                EGL_NO_CONTEXT);
164
165        if (mEglContext != EGL_NO_CONTEXT) {
166            eglDestroyContext(mEglDisplay, mEglContext);
167        }
168
169        if (mEglSurface != EGL_NO_SURFACE) {
170            eglDestroySurface(mEglDisplay, mEglSurface);
171        }
172    }
173
174    mEglDisplay = EGL_NO_DISPLAY;
175    mEglContext = EGL_NO_CONTEXT;
176    mEglSurface = EGL_NO_SURFACE;
177    mEglConfig = NULL;
178
179    eglReleaseThread();
180}
181
182// Sets the presentation time on the current EGL buffer.
183void EglWindow::presentationTime(nsecs_t whenNsec) const {
184    eglPresentationTimeANDROID(mEglDisplay, mEglSurface, whenNsec);
185}
186
187// Swaps the EGL buffer.
188void EglWindow::swapBuffers() const {
189    eglSwapBuffers(mEglDisplay, mEglSurface);
190}
191