1 /*
2 * Copyright (C) 2011 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 #include "EmulatedEglWindowSurface.h"
17 
18 #include <assert.h>
19 #include <ios>
20 #include <stdio.h>
21 #include <string.h>
22 
23 #include <GLES/glext.h>
24 
25 #include "OpenGLESDispatch/DispatchTables.h"
26 #include "OpenGLESDispatch/EGLDispatch.h"
27 #include "aemu/base/containers/Lookup.h"
28 #include "host-common/GfxstreamFatalError.h"
29 #include "host-common/logging.h"
30 
31 using emugl::ABORT_REASON_OTHER;
32 using emugl::FatalError;
33 
34 namespace gfxstream {
35 namespace gl {
36 
EmulatedEglWindowSurface(EGLDisplay display,EGLConfig config,HandleType hndl)37 EmulatedEglWindowSurface::EmulatedEglWindowSurface(EGLDisplay display,
38                                                    EGLConfig config,
39                                                    HandleType hndl) :
40         mConfig(config),
41         mDisplay(display),
42         mHndl(hndl) {}
43 
~EmulatedEglWindowSurface()44 EmulatedEglWindowSurface::~EmulatedEglWindowSurface() {
45     if (mSurface) {
46         s_egl.eglDestroySurface(mDisplay, mSurface);
47     }
48 }
49 
create(EGLDisplay display,EGLConfig config,int p_width,int p_height,HandleType hndl)50 std::unique_ptr<EmulatedEglWindowSurface> EmulatedEglWindowSurface::create(
51         EGLDisplay display,
52         EGLConfig config,
53         int p_width,
54         int p_height,
55         HandleType hndl) {
56     std::unique_ptr<EmulatedEglWindowSurface> surface(
57         new EmulatedEglWindowSurface(display, config, hndl));
58 
59     // Create a pbuffer to be used as the egl surface for that window.
60     if (!surface->resize(p_width, p_height)) {
61         return nullptr;
62     }
63 
64     return surface;
65 }
66 
setColorBuffer(ColorBufferPtr p_colorBuffer)67 void EmulatedEglWindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer) {
68     mAttachedColorBuffer = p_colorBuffer;
69     if (!p_colorBuffer) return;
70 
71     // resize the window if the attached color buffer is of different
72     // size.
73     unsigned int cbWidth = mAttachedColorBuffer->getWidth();
74     unsigned int cbHeight = mAttachedColorBuffer->getHeight();
75 
76     if (cbWidth != mWidth || cbHeight != mHeight) {
77         resize(cbWidth, cbHeight);
78     }
79 }
80 
bind(EmulatedEglContextPtr p_ctx,BindType p_bindType)81 void EmulatedEglWindowSurface::bind(EmulatedEglContextPtr p_ctx, BindType p_bindType) {
82     if (p_bindType == BIND_READ) {
83         mReadContext = p_ctx;
84     } else if (p_bindType == BIND_DRAW) {
85         mDrawContext = p_ctx;
86     } else if (p_bindType == BIND_READDRAW) {
87         mReadContext = p_ctx;
88         mDrawContext = p_ctx;
89     }
90 }
91 
getWidth() const92 GLuint EmulatedEglWindowSurface::getWidth() const { return mWidth; }
getHeight() const93 GLuint EmulatedEglWindowSurface::getHeight() const { return mHeight; }
94 
flushColorBuffer()95 bool EmulatedEglWindowSurface::flushColorBuffer() {
96     if (!mAttachedColorBuffer.get()) {
97         return true;
98     }
99     if (!mWidth || !mHeight) {
100         return false;
101     }
102 
103     if (mAttachedColorBuffer->getWidth() != mWidth ||
104         mAttachedColorBuffer->getHeight() != mHeight) {
105         // XXX: should never happen - how this needs to be handled?
106         ERR("Dimensions do not match");
107         return false;
108     }
109 
110     if (!mDrawContext.get()) {
111         ERR("%p: Draw context is NULL", this);
112         return false;
113     }
114 
115     GLenum resetStatus = s_gles2.glGetGraphicsResetStatusEXT();
116     if (resetStatus != GL_NO_ERROR) {
117         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
118                 "Stream server aborting due to graphics reset. ResetStatus: " <<
119                 std::hex << resetStatus;
120     }
121 
122     // Make the surface current
123     EGLContext prevContext = s_egl.eglGetCurrentContext();
124     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
125     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
126 
127     const bool needToSet = prevContext != mDrawContext->getEGLContext() ||
128                            prevReadSurf != mSurface || prevDrawSurf != mSurface;
129     if (needToSet) {
130         if (!s_egl.eglMakeCurrent(mDisplay,
131                                   mSurface,
132                                   mSurface,
133                                   mDrawContext->getEGLContext())) {
134             ERR("Error making draw context current");
135             return false;
136         }
137     }
138 
139     mAttachedColorBuffer->glOpBlitFromCurrentReadBuffer();
140 
141     if (needToSet) {
142         // restore current context/surface
143         s_egl.eglMakeCurrent(mDisplay, prevDrawSurf, prevReadSurf, prevContext);
144     }
145 
146     return true;
147 }
148 
resize(unsigned int p_width,unsigned int p_height)149 bool EmulatedEglWindowSurface::resize(unsigned int p_width, unsigned int p_height)
150 {
151     if (mSurface && mWidth == p_width && mHeight == p_height) {
152         // no need to resize
153         return true;
154     }
155 
156     EGLContext prevContext = s_egl.eglGetCurrentContext();
157     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
158     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
159     EGLSurface prevPbuf = mSurface;
160     bool needRebindContext = mSurface &&
161                              (prevReadSurf == mSurface ||
162                               prevDrawSurf == mSurface);
163 
164     if (needRebindContext) {
165         s_egl.eglMakeCurrent(
166                 mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
167     }
168 
169     //
170     // Destroy previous surface
171     //
172     if (mSurface) {
173         s_egl.eglDestroySurface(mDisplay, mSurface);
174         mSurface = NULL;
175     }
176 
177     //
178     // Create pbuffer surface.
179     //
180     const EGLint pbufAttribs[5] = {
181         EGL_WIDTH, (EGLint) p_width, EGL_HEIGHT, (EGLint) p_height, EGL_NONE,
182     };
183 
184     mSurface = s_egl.eglCreatePbufferSurface(mDisplay,
185                                              mConfig,
186                                              pbufAttribs);
187     if (mSurface == EGL_NO_SURFACE) {
188         ERR("Renderer error: failed to create/resize pbuffer!!");
189         return false;
190     }
191 
192     mWidth = p_width;
193     mHeight = p_height;
194 
195     if (needRebindContext) {
196         s_egl.eglMakeCurrent(
197                 mDisplay,
198                 (prevDrawSurf == prevPbuf) ? mSurface : prevDrawSurf,
199                 (prevReadSurf == prevPbuf) ? mSurface : prevReadSurf,
200                 prevContext);
201     }
202 
203     return true;
204 }
205 
getHndl() const206 HandleType EmulatedEglWindowSurface::getHndl() const {
207     return mHndl;
208 }
209 
210 template <class obj_t>
saveHndlOrNull(obj_t obj,android::base::Stream * stream)211 static void saveHndlOrNull(obj_t obj, android::base::Stream* stream) {
212     if (obj) {
213         stream->putBe32(obj->getHndl());
214     } else {
215         stream->putBe32(0);
216     }
217 }
218 
onSave(android::base::Stream * stream) const219 void EmulatedEglWindowSurface::onSave(android::base::Stream* stream) const {
220     stream->putBe32(getHndl());
221     saveHndlOrNull(mAttachedColorBuffer, stream);
222     saveHndlOrNull(mReadContext, stream);
223     saveHndlOrNull(mDrawContext, stream);
224     stream->putBe32(mWidth);
225     stream->putBe32(mHeight);
226     if (s_egl.eglSaveConfig) {
227         s_egl.eglSaveConfig(mDisplay, mConfig, stream);
228     }
229 }
230 
onLoad(android::base::Stream * stream,EGLDisplay display,const ColorBufferMap & colorBuffers,const EmulatedEglContextMap & contexts)231 std::unique_ptr<EmulatedEglWindowSurface> EmulatedEglWindowSurface::onLoad(
232         android::base::Stream* stream,
233         EGLDisplay display,
234         const ColorBufferMap& colorBuffers,
235         const EmulatedEglContextMap& contexts) {
236     HandleType hndl = stream->getBe32();
237     HandleType colorBufferHndl = stream->getBe32();
238     HandleType readCtx = stream->getBe32();
239     HandleType drawCtx = stream->getBe32();
240 
241     GLuint width = stream->getBe32();
242     GLuint height = stream->getBe32();
243     EGLConfig config = 0;
244     if (s_egl.eglLoadConfig) {
245         config = s_egl.eglLoadConfig(display, stream);
246     }
247 
248     auto surface = create(display, config, width, height, hndl);
249     assert(surface);
250     // fb is already locked by its caller
251     if (colorBufferHndl) {
252         const auto* colorBufferRef = android::base::find(colorBuffers, colorBufferHndl);
253         assert(colorBufferRef);
254         surface->mAttachedColorBuffer = colorBufferRef->cb;
255     }
256     surface->mReadContext = android::base::findOrDefault(contexts, readCtx);
257     surface->mDrawContext = android::base::findOrDefault(contexts, drawCtx);
258     return surface;
259 }
260 
261 }  // namespace gl
262 }  // namespace gfxstream
263