1 // Copyright (C) 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "DisplaySurfaceGl.h"
16
17 #include <vector>
18
19 #include <stdio.h>
20
21 #include "OpenGLESDispatch/DispatchTables.h"
22 #include "OpenGLESDispatch/EGLDispatch.h"
23 #include "host-common/GfxstreamFatalError.h"
24 #include "host-common/logging.h"
25
26 namespace gfxstream {
27 namespace gl {
28 namespace {
29
30 using emugl::ABORT_REASON_OTHER;
31 using emugl::FatalError;
32
33 struct PreviousContextInfo {
34 EGLContext context = EGL_NO_CONTEXT;
35 EGLSurface readSurface = EGL_NO_SURFACE;
36 EGLSurface drawSurface = EGL_NO_SURFACE;
37 };
38
39 struct ThreadState {
40 std::vector<PreviousContextInfo> previousContexts;
41 };
42
43 static thread_local ThreadState sThreadState;
44
45 class DisplaySurfaceGlContextHelper : public ContextHelper {
46 public:
DisplaySurfaceGlContextHelper(EGLDisplay display,EGLSurface surface,EGLContext context)47 DisplaySurfaceGlContextHelper(EGLDisplay display,
48 EGLSurface surface,
49 EGLContext context)
50 : mDisplay(display),
51 mSurface(surface),
52 mContext(context) {
53 if (mDisplay == EGL_NO_DISPLAY) {
54 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
55 << "DisplaySurfaceGlContextHelper created with no display?";
56 }
57 if (mSurface == EGL_NO_SURFACE) {
58 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
59 << "DisplaySurfaceGlContextHelper created with no surface?";
60 }
61 if (mContext == EGL_NO_CONTEXT) {
62 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
63 << "DisplaySurfaceGlContextHelper created with no context?";
64 }
65 }
66
setupContext()67 bool setupContext() override {
68 auto& previousContexts = sThreadState.previousContexts;
69
70 EGLContext currentContext = s_egl.eglGetCurrentContext();
71 EGLSurface currentDrawSurface = s_egl.eglGetCurrentSurface(EGL_DRAW);
72 EGLSurface currentReadSurface = s_egl.eglGetCurrentSurface(EGL_READ);
73
74 bool needsUpdate = (currentContext != mContext ||
75 currentDrawSurface != mSurface ||
76 currentReadSurface != mSurface);
77
78 if (needsUpdate) {
79 if (!previousContexts.empty()) {
80 ERR("DisplaySurfaceGlContextHelper context was preempted by others, "
81 "current=%p, needed=%p, thread=%p", currentContext, mContext, &sThreadState);
82 // Fall through to attempt to recover from error.
83 }
84
85 if (!s_egl.eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
86 // b/284523053
87 // Legacy swiftshader logspam on exit with this line.
88 GL_LOG("Failed to make display surface context current: %d", s_egl.eglGetError());
89 // Fall through to allow adding previous context to stack.
90 }
91 }
92
93 previousContexts.push_back(
94 {.context = currentContext,
95 .readSurface = currentReadSurface,
96 .drawSurface = currentDrawSurface});
97 return true;
98 }
99
teardownContext()100 void teardownContext() override {
101 auto& previousContexts = sThreadState.previousContexts;
102
103 EGLContext currentContext = s_egl.eglGetCurrentContext();
104 EGLSurface currentDrawSurface = s_egl.eglGetCurrentSurface(EGL_DRAW);
105 EGLSurface currentReadSurface = s_egl.eglGetCurrentSurface(EGL_READ);
106
107 PreviousContextInfo newContext;
108 if (!previousContexts.empty()) {
109 newContext = previousContexts.back();
110 previousContexts.pop_back();
111 }
112
113 bool needsUpdate = (currentContext != newContext.context ||
114 currentDrawSurface != newContext.drawSurface ||
115 currentReadSurface != newContext.readSurface);
116
117 if (!needsUpdate) {
118 return;
119 }
120
121 if (!s_egl.eglMakeCurrent(mDisplay,
122 newContext.drawSurface,
123 newContext.readSurface,
124 newContext.context)) {
125 ERR("Failed to restore previous context: %d", s_egl.eglGetError());
126 }
127 }
128
isBound() const129 bool isBound() const override { return !sThreadState.previousContexts.empty(); }
130
131 private:
132 EGLDisplay mDisplay = EGL_NO_DISPLAY;
133 EGLSurface mSurface = EGL_NO_SURFACE;
134 EGLContext mContext = EGL_NO_CONTEXT;
135 };
136
137 } // namespace
138
139 /*static*/
createPbufferSurface(EGLDisplay display,EGLConfig config,EGLContext shareContext,const EGLint * contextAttribs,EGLint width,EGLint height)140 std::unique_ptr<DisplaySurfaceGl> DisplaySurfaceGl::createPbufferSurface(
141 EGLDisplay display,
142 EGLConfig config,
143 EGLContext shareContext,
144 const EGLint* contextAttribs,
145 EGLint width,
146 EGLint height) {
147 EGLContext context = s_egl.eglCreateContext(display, config, shareContext, contextAttribs);
148 if (context == EGL_NO_CONTEXT) {
149 ERR("Failed to create context for DisplaySurfaceGl.");
150 return nullptr;
151 }
152
153 const EGLint surfaceAttribs[] = {
154 EGL_WIDTH, width, //
155 EGL_HEIGHT, height, //
156 EGL_NONE, //
157 };
158 EGLSurface surface = s_egl.eglCreatePbufferSurface(display, config, surfaceAttribs);
159 if (surface == EGL_NO_SURFACE) {
160 ERR("Failed to create pbuffer surface for DisplaySurfaceGl.");
161 return nullptr;
162 }
163
164 return std::unique_ptr<DisplaySurfaceGl>(new DisplaySurfaceGl(display, surface, context));
165 }
166
167 /*static*/
createWindowSurface(EGLDisplay display,EGLConfig config,EGLContext shareContext,const GLint * contextAttribs,FBNativeWindowType window)168 std::unique_ptr<DisplaySurfaceGl> DisplaySurfaceGl::createWindowSurface(
169 EGLDisplay display,
170 EGLConfig config,
171 EGLContext shareContext,
172 const GLint* contextAttribs,
173 FBNativeWindowType window) {
174 EGLContext context = s_egl.eglCreateContext(display, config, shareContext, contextAttribs);
175 if (context == EGL_NO_CONTEXT) {
176 ERR("Failed to create context for DisplaySurfaceGl.");
177 return nullptr;
178 }
179
180 EGLSurface surface = s_egl.eglCreateWindowSurface(display, config, window, nullptr);
181 if (surface == EGL_NO_SURFACE) {
182 ERR("Failed to create window surface for DisplaySurfaceGl.");
183 return nullptr;
184 }
185
186 return std::unique_ptr<DisplaySurfaceGl>(new DisplaySurfaceGl(display, surface, context));
187 }
188
bindContext() const189 bool DisplaySurfaceGl::bindContext() const {
190 if (!s_egl.eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
191 ERR("Failed to make display surface context current: %d", s_egl.eglGetError());
192 return false;
193 }
194 return true;
195 }
196
DisplaySurfaceGl(EGLDisplay display,EGLSurface surface,EGLContext context)197 DisplaySurfaceGl::DisplaySurfaceGl(EGLDisplay display,
198 EGLSurface surface,
199 EGLContext context)
200 : mDisplay(display),
201 mSurface(surface),
202 mContext(context),
203 mContextHelper(new DisplaySurfaceGlContextHelper(display, surface, context)) {}
204
~DisplaySurfaceGl()205 DisplaySurfaceGl::~DisplaySurfaceGl() {
206 if (mDisplay != EGL_NO_DISPLAY) {
207 if (mSurface) {
208 s_egl.eglDestroySurface(mDisplay, mSurface);
209 }
210 if (mContext) {
211 s_egl.eglDestroyContext(mDisplay, mContext);
212 }
213 }
214 }
215
216 } // namespace gl
217 } // namespace gfxstream