1 /*
2 * Copyright (C) 2023 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 "PostWorkerGl.h"
17
18 #include "FrameBuffer.h"
19 #include "gl/DisplayGl.h"
20 #include "gl/DisplaySurfaceGl.h"
21 #include "host-common/GfxstreamFatalError.h"
22 #include "host-common/logging.h"
23 #include "host-common/misc.h"
24
25 namespace gfxstream {
26
27 namespace {
28
29 using emugl::ABORT_REASON_OTHER;
30 using emugl::FatalError;
31 using gl::DisplayGl;
32 using gl::DisplaySurfaceGl;
33 using gl::EmulationGl;
34 using gl::s_egl;
35
getTransformFromRotation(int rotation)36 hwc_transform_t getTransformFromRotation(int rotation) {
37 switch (static_cast<int>(rotation / 90)) {
38 case 1:
39 return HWC_TRANSFORM_ROT_270;
40 case 2:
41 return HWC_TRANSFORM_ROT_180;
42 case 3:
43 return HWC_TRANSFORM_ROT_90;
44 default:
45 return HWC_TRANSFORM_NONE;
46 }
47 }
48
49 } // namespace
50
PostWorkerGl(bool mainThreadPostingOnly,FrameBuffer * fb,Compositor * compositor,DisplayGl * displayGl,gl::EmulationGl * emulationGl)51 PostWorkerGl::PostWorkerGl(bool mainThreadPostingOnly, FrameBuffer* fb, Compositor* compositor,
52 DisplayGl* displayGl, gl::EmulationGl* emulationGl)
53 : PostWorker(mainThreadPostingOnly, fb, compositor),
54 m_displayGl(displayGl),
55 mEmulationGl(emulationGl) {
56 if (!m_displayGl) {
57 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "PostWorker missing DisplayGl.";
58 }
59 }
60
screenshot(ColorBuffer * cb,int screenwidth,int screenheight,GLenum format,GLenum type,int skinRotation,void * pixels,Rect rect)61 void PostWorkerGl::screenshot(ColorBuffer* cb, int screenwidth, int screenheight, GLenum format,
62 GLenum type, int skinRotation, void* pixels, Rect rect) {
63 // See b/292237104.
64 mFb->lock();
65 cb->readToBytesScaled(screenwidth, screenheight, format, type, skinRotation, rect, pixels);
66 mFb->unlock();
67 }
68
postImpl(ColorBuffer * cb)69 std::shared_future<void> PostWorkerGl::postImpl(ColorBuffer* cb) {
70 if (!mContextBound || m_mainThreadPostingOnly) {
71 // This might happen on headless mode
72 // Also if posting on main thread, the context binding can get polluted easily, which
73 // requires frequent rebinds.
74 setupContext();
75 }
76 std::shared_future<void> completedFuture = std::async(std::launch::deferred, [] {}).share();
77 completedFuture.wait();
78
79 DisplayGl::Post post = {};
80
81 ComposeLayer postLayerOptions = {
82 .composeMode = HWC2_COMPOSITION_DEVICE,
83 .blendMode = HWC2_BLEND_MODE_NONE,
84 .alpha = 1.0f,
85 .transform = HWC_TRANSFORM_NONE,
86 };
87
88 const auto& multiDisplay = emugl::get_emugl_multi_display_operations();
89 const bool pixel_fold = multiDisplay.isPixelFold();
90 if (pixel_fold) {
91 post.layers.push_back(postWithOverlay(cb));
92 }
93 else if (multiDisplay.isMultiDisplayEnabled()) {
94 if (multiDisplay.isMultiDisplayWindow()) {
95 int32_t previousDisplayId = -1;
96 uint32_t currentDisplayId;
97 uint32_t currentDisplayColorBufferHandle;
98 while (multiDisplay.getNextMultiDisplay(previousDisplayId, ¤tDisplayId,
99 /*x=*/nullptr,
100 /*y=*/nullptr,
101 /*w=*/nullptr,
102 /*h=*/nullptr,
103 /*dpi=*/nullptr,
104 /*flags=*/nullptr,
105 ¤tDisplayColorBufferHandle)) {
106 previousDisplayId = currentDisplayId;
107
108 if (currentDisplayColorBufferHandle == 0) {
109 continue;
110 }
111 emugl::get_emugl_window_operations().paintMultiDisplayWindow(
112 currentDisplayId, currentDisplayColorBufferHandle);
113 }
114 post.layers.push_back(postWithOverlay(cb));
115 } else {
116 uint32_t combinedDisplayW = 0;
117 uint32_t combinedDisplayH = 0;
118 multiDisplay.getCombinedDisplaySize(&combinedDisplayW, &combinedDisplayH);
119
120 post.frameWidth = combinedDisplayW;
121 post.frameHeight = combinedDisplayH;
122
123 int32_t previousDisplayId = -1;
124 uint32_t currentDisplayId;
125 int32_t currentDisplayOffsetX;
126 int32_t currentDisplayOffsetY;
127 uint32_t currentDisplayW;
128 uint32_t currentDisplayH;
129 uint32_t currentDisplayColorBufferHandle;
130 while (multiDisplay.getNextMultiDisplay(
131 previousDisplayId, ¤tDisplayId, ¤tDisplayOffsetX,
132 ¤tDisplayOffsetY, ¤tDisplayW, ¤tDisplayH,
133 /*dpi=*/nullptr,
134 /*flags=*/nullptr, ¤tDisplayColorBufferHandle)) {
135 previousDisplayId = currentDisplayId;
136
137 if (currentDisplayW == 0 || currentDisplayH == 0 ||
138 (currentDisplayId != 0 && currentDisplayColorBufferHandle == 0)) {
139 continue;
140 }
141
142 ColorBuffer* currentCb =
143 currentDisplayId == 0
144 ? cb
145 : mFb->findColorBuffer(currentDisplayColorBufferHandle).get();
146 if (!currentCb) {
147 continue;
148 }
149
150 const auto transform = getTransformFromRotation(mFb->getZrot());
151 postLayerOptions.transform = transform;
152 if ( transform == HWC_TRANSFORM_ROT_90 || transform == HWC_TRANSFORM_ROT_270) {
153 std::swap(currentDisplayW, currentDisplayH);
154 }
155 postLayerOptions.displayFrame = {
156 .left = static_cast<int>(currentDisplayOffsetX),
157 .top = static_cast<int>(currentDisplayOffsetY),
158 .right = static_cast<int>(currentDisplayOffsetX + currentDisplayW),
159 .bottom = static_cast<int>(currentDisplayOffsetY + currentDisplayH),
160 };
161 postLayerOptions.crop = {
162 .left = 0.0f,
163 .top = static_cast<float>(currentCb->getHeight()),
164 .right = static_cast<float>(currentCb->getWidth()),
165 .bottom = 0.0f,
166 };
167
168 post.layers.push_back(DisplayGl::PostLayer{
169 .colorBuffer = currentCb,
170 .layerOptions = postLayerOptions,
171 });
172 }
173 }
174 } else if (emugl::get_emugl_window_operations().isFolded()) {
175 const float dpr = mFb->getDpr();
176
177 post.frameWidth = m_viewportWidth / dpr;
178 post.frameHeight = m_viewportHeight / dpr;
179
180 int displayOffsetX;
181 int displayOffsetY;
182 int displayW;
183 int displayH;
184 emugl::get_emugl_window_operations().getFoldedArea(&displayOffsetX, &displayOffsetY,
185 &displayW, &displayH);
186
187 postLayerOptions.displayFrame = {
188 .left = 0,
189 .top = 0,
190 .right = mFb->windowWidth(),
191 .bottom = mFb->windowHeight(),
192 };
193 postLayerOptions.crop = {
194 .left = static_cast<float>(displayOffsetX),
195 .top = static_cast<float>(displayOffsetY + displayH),
196 .right = static_cast<float>(displayOffsetX + displayW),
197 .bottom = static_cast<float>(displayOffsetY),
198 };
199 postLayerOptions.transform = getTransformFromRotation(mFb->getZrot());
200
201 post.layers.push_back(DisplayGl::PostLayer{
202 .colorBuffer = cb,
203 .layerOptions = postLayerOptions,
204 });
205 } else {
206 post.layers.push_back(postWithOverlay(cb));
207 }
208 return m_displayGl->post(post);
209 }
210
postWithOverlay(ColorBuffer * cb)211 DisplayGl::PostLayer PostWorkerGl::postWithOverlay(ColorBuffer* cb) {
212 float dpr = mFb->getDpr();
213 int windowWidth = mFb->windowWidth();
214 int windowHeight = mFb->windowHeight();
215 float px = mFb->getPx();
216 float py = mFb->getPy();
217 int zRot = mFb->getZrot();
218 hwc_transform_t rotation = (hwc_transform_t)0;
219
220 // Find the x and y values at the origin when "fully scrolled."
221 // Multiply by 2 because the texture goes from -1 to 1, not 0 to 1.
222 // Multiply the windowing coordinates by DPR because they ignore
223 // DPR, but the viewport includes DPR.
224 float fx = 2.f * (m_viewportWidth - windowWidth * dpr) / (float)m_viewportWidth;
225 float fy = 2.f * (m_viewportHeight - windowHeight * dpr) / (float)m_viewportHeight;
226
227 // finally, compute translation values
228 float dx = px * fx;
229 float dy = py * fy;
230
231 return DisplayGl::PostLayer{
232 .colorBuffer = cb,
233 .overlayOptions =
234 DisplayGl::PostLayer::OverlayOptions{
235 .rotation = static_cast<float>(zRot),
236 .dx = dx,
237 .dy = dy,
238 },
239 };
240 }
241
242 // Called whenever the subwindow needs a refresh (FrameBuffer::setupSubWindow).
243 // This rebinds the subwindow context (to account for
244 // when the refresh is a display change, for instance)
245 // and resets the posting viewport.
viewportImpl(int width,int height)246 void PostWorkerGl::viewportImpl(int width, int height) {
247 setupContext();
248 const float dpr = mFb->getDpr();
249 m_viewportWidth = width * dpr;
250 m_viewportHeight = height * dpr;
251
252 if (!m_displayGl) {
253 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "PostWorker missing DisplayGl.";
254 }
255 m_displayGl->viewport(m_viewportWidth, m_viewportHeight);
256 }
257
258 // Called when the subwindow refreshes, but there is no
259 // last posted color buffer to show to the user. Instead of
260 // displaying whatever happens to be in the back buffer,
261 // clear() is useful for outputting consistent colors.
clearImpl()262 void PostWorkerGl::clearImpl() {
263 if (!mContextBound || m_mainThreadPostingOnly) {
264 // This might happen on headless mode
265 setupContext();
266 }
267 m_displayGl->clear();
268 }
269
composeImpl(const FlatComposeRequest & composeRequest)270 std::shared_future<void> PostWorkerGl::composeImpl(const FlatComposeRequest& composeRequest) {
271 if (!mContextBound || m_mainThreadPostingOnly) {
272 // This might happen on headless mode
273 setupContext();
274 }
275 return PostWorker::composeImpl(composeRequest);
276 }
277
setupContext()278 void PostWorkerGl::setupContext() {
279 android::base::AutoLock lock(mMutex);
280 const auto* surface = getBoundSurface();
281 const DisplaySurfaceGl* surfaceGl = nullptr;
282 if (surface) {
283 surfaceGl = static_cast<const DisplaySurfaceGl*>(surface->getImpl());
284 } else {
285 // Create a fake context.
286 // This could happen in AEMU with -qt-hide-window. Also due to an Intel bug
287 // this needs to happen on post thread.
288 // b/274313125
289 if (!mFakeWindowSurface) {
290 mFakeWindowSurface = mEmulationGl->createFakeWindowSurface();
291 }
292 if (!mFakeWindowSurface) {
293 ERR("Post worker does not have a window surface.");
294 return;
295 }
296 surfaceGl = static_cast<const DisplaySurfaceGl*>(mFakeWindowSurface->getImpl());
297 }
298
299 // It will be no-op if it rebinds to the same context.
300 // We need to retry just in case the surface is changed.
301
302 // Also we could not use the scope context helper here,
303 // because (1) binds and unbinds happen in very different places;
304 // (2) they both need to happen in post thread, but the d'tor
305 // of PostWorker can happen in a different thread.
306 if (!surfaceGl->bindContext()) {
307 ERR("Failed to bind to post worker context.");
308 return;
309 }
310 mContextBound = true;
311 }
312
exitImpl()313 void PostWorkerGl::exitImpl() {
314 if (!mContextBound) {
315 return;
316 }
317 s_egl.eglMakeCurrent(s_egl.eglGetDisplay(EGL_DEFAULT_DISPLAY), nullptr, nullptr, nullptr);
318 mContextBound = false;
319 }
320
321 } // namespace gfxstream
322