• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef CONFIG_AEMU
92         if (emugl::shouldSkipDraw()) {
93             post.layers.clear();
94         } else {
95             post.layers.push_back(postWithOverlay(cb));
96         }
97 #endif
98     }
99     else if (multiDisplay.isMultiDisplayEnabled()) {
100         if (multiDisplay.isMultiDisplayWindow()) {
101             int32_t previousDisplayId = -1;
102             uint32_t currentDisplayId;
103             uint32_t currentDisplayColorBufferHandle;
104             while (multiDisplay.getNextMultiDisplay(previousDisplayId, &currentDisplayId,
105                                                     /*x=*/nullptr,
106                                                     /*y=*/nullptr,
107                                                     /*w=*/nullptr,
108                                                     /*h=*/nullptr,
109                                                     /*dpi=*/nullptr,
110                                                     /*flags=*/nullptr,
111                                                     &currentDisplayColorBufferHandle)) {
112                 previousDisplayId = currentDisplayId;
113 
114                 if (currentDisplayColorBufferHandle == 0) {
115                     continue;
116                 }
117                 emugl::get_emugl_window_operations().paintMultiDisplayWindow(
118                     currentDisplayId, currentDisplayColorBufferHandle);
119             }
120             post.layers.push_back(postWithOverlay(cb));
121         } else {
122             uint32_t combinedDisplayW = 0;
123             uint32_t combinedDisplayH = 0;
124             multiDisplay.getCombinedDisplaySize(&combinedDisplayW, &combinedDisplayH);
125 
126             post.frameWidth = combinedDisplayW;
127             post.frameHeight = combinedDisplayH;
128 
129             int32_t previousDisplayId = -1;
130             uint32_t currentDisplayId;
131             int32_t currentDisplayOffsetX;
132             int32_t currentDisplayOffsetY;
133             uint32_t currentDisplayW;
134             uint32_t currentDisplayH;
135             uint32_t currentDisplayColorBufferHandle;
136             while (multiDisplay.getNextMultiDisplay(
137                 previousDisplayId, &currentDisplayId, &currentDisplayOffsetX,
138                 &currentDisplayOffsetY, &currentDisplayW, &currentDisplayH,
139                 /*dpi=*/nullptr,
140                 /*flags=*/nullptr, &currentDisplayColorBufferHandle)) {
141                 previousDisplayId = currentDisplayId;
142 
143                 if (currentDisplayW == 0 || currentDisplayH == 0 ||
144                     (currentDisplayId != 0 && currentDisplayColorBufferHandle == 0)) {
145                     continue;
146                 }
147 
148                 ColorBuffer* currentCb =
149                     currentDisplayId == 0
150                         ? cb
151                         : mFb->findColorBuffer(currentDisplayColorBufferHandle).get();
152                 if (!currentCb) {
153                     continue;
154                 }
155 
156                 const auto transform = getTransformFromRotation(mFb->getZrot());
157                 postLayerOptions.transform = transform;
158                 if ( transform == HWC_TRANSFORM_ROT_90 || transform == HWC_TRANSFORM_ROT_270) {
159                     std::swap(currentDisplayW, currentDisplayH);
160                 }
161                 postLayerOptions.displayFrame = {
162                     .left = static_cast<int>(currentDisplayOffsetX),
163                     .top = static_cast<int>(currentDisplayOffsetY),
164                     .right = static_cast<int>(currentDisplayOffsetX + currentDisplayW),
165                     .bottom = static_cast<int>(currentDisplayOffsetY + currentDisplayH),
166                 };
167                 postLayerOptions.crop = {
168                     .left = 0.0f,
169                     .top = static_cast<float>(currentCb->getHeight()),
170                     .right = static_cast<float>(currentCb->getWidth()),
171                     .bottom = 0.0f,
172                 };
173 
174                 post.layers.push_back(DisplayGl::PostLayer{
175                     .colorBuffer = currentCb,
176                     .layerOptions = postLayerOptions,
177                 });
178             }
179         }
180     } else if (emugl::get_emugl_window_operations().isFolded()) {
181         const float dpr = mFb->getDpr();
182 
183         post.frameWidth = m_viewportWidth / dpr;
184         post.frameHeight = m_viewportHeight / dpr;
185 
186         int displayOffsetX;
187         int displayOffsetY;
188         int displayW;
189         int displayH;
190         emugl::get_emugl_window_operations().getFoldedArea(&displayOffsetX, &displayOffsetY,
191                                                            &displayW, &displayH);
192 
193         postLayerOptions.displayFrame = {
194             .left = 0,
195             .top = 0,
196             .right = mFb->windowWidth(),
197             .bottom = mFb->windowHeight(),
198         };
199         postLayerOptions.crop = {
200             .left = static_cast<float>(displayOffsetX),
201             .top = static_cast<float>(displayOffsetY + displayH),
202             .right = static_cast<float>(displayOffsetX + displayW),
203             .bottom = static_cast<float>(displayOffsetY),
204         };
205         postLayerOptions.transform = getTransformFromRotation(mFb->getZrot());
206 
207         post.layers.push_back(DisplayGl::PostLayer{
208             .colorBuffer = cb,
209             .layerOptions = postLayerOptions,
210         });
211     } else {
212         post.layers.push_back(postWithOverlay(cb));
213     }
214     return m_displayGl->post(post);
215 }
216 
postWithOverlay(ColorBuffer * cb)217 DisplayGl::PostLayer PostWorkerGl::postWithOverlay(ColorBuffer* cb) {
218     float dpr = mFb->getDpr();
219     int windowWidth = mFb->windowWidth();
220     int windowHeight = mFb->windowHeight();
221     float px = mFb->getPx();
222     float py = mFb->getPy();
223     int zRot = mFb->getZrot();
224     hwc_transform_t rotation = (hwc_transform_t)0;
225 
226     // Find the x and y values at the origin when "fully scrolled."
227     // Multiply by 2 because the texture goes from -1 to 1, not 0 to 1.
228     // Multiply the windowing coordinates by DPR because they ignore
229     // DPR, but the viewport includes DPR.
230     float fx = 2.f * (m_viewportWidth - windowWidth * dpr) / (float)m_viewportWidth;
231     float fy = 2.f * (m_viewportHeight - windowHeight * dpr) / (float)m_viewportHeight;
232 
233     // finally, compute translation values
234     float dx = px * fx;
235     float dy = py * fy;
236 
237     return DisplayGl::PostLayer{
238         .colorBuffer = cb,
239         .overlayOptions =
240             DisplayGl::PostLayer::OverlayOptions{
241                 .rotation = static_cast<float>(zRot),
242                 .dx = dx,
243                 .dy = dy,
244             },
245     };
246 }
247 
248 // Called whenever the subwindow needs a refresh (FrameBuffer::setupSubWindow).
249 // This rebinds the subwindow context (to account for
250 // when the refresh is a display change, for instance)
251 // and resets the posting viewport.
viewportImpl(int width,int height)252 void PostWorkerGl::viewportImpl(int width, int height) {
253     setupContext();
254     const float dpr = mFb->getDpr();
255     m_viewportWidth = width * dpr;
256     m_viewportHeight = height * dpr;
257 
258     if (!m_displayGl) {
259         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "PostWorker missing DisplayGl.";
260     }
261     m_displayGl->viewport(m_viewportWidth, m_viewportHeight);
262 }
263 
264 // Called when the subwindow refreshes, but there is no
265 // last posted color buffer to show to the user. Instead of
266 // displaying whatever happens to be in the back buffer,
267 // clear() is useful for outputting consistent colors.
clearImpl()268 void PostWorkerGl::clearImpl() {
269     if (!mContextBound || m_mainThreadPostingOnly) {
270         // This might happen on headless mode
271         setupContext();
272     }
273     m_displayGl->clear();
274 }
275 
composeImpl(const FlatComposeRequest & composeRequest)276 std::shared_future<void> PostWorkerGl::composeImpl(const FlatComposeRequest& composeRequest) {
277     if (!mContextBound || m_mainThreadPostingOnly) {
278         // This might happen on headless mode
279         setupContext();
280     }
281     return PostWorker::composeImpl(composeRequest);
282 }
283 
setupContext()284 void PostWorkerGl::setupContext() {
285     android::base::AutoLock lock(mMutex);
286     const auto* surface = getBoundSurface();
287     const DisplaySurfaceGl* surfaceGl = nullptr;
288     if (surface) {
289         surfaceGl = static_cast<const DisplaySurfaceGl*>(surface->getImpl());
290     } else {
291         // Create a fake context.
292         // This could happen in AEMU with -qt-hide-window. Also due to an Intel bug
293         // this needs to happen on post thread.
294         // b/274313125
295         if (!mFakeWindowSurface) {
296             mFakeWindowSurface = mEmulationGl->createFakeWindowSurface();
297         }
298         if (!mFakeWindowSurface) {
299             ERR("Post worker does not have a window surface.");
300             return;
301         }
302         surfaceGl = static_cast<const DisplaySurfaceGl*>(mFakeWindowSurface->getImpl());
303     }
304 
305     // It will be no-op if it rebinds to the same context.
306     // We need to retry just in case the surface is changed.
307 
308     // Also we could not use the scope context helper here,
309     // because (1) binds and unbinds happen in very different places;
310     // (2) they both need to happen in post thread, but the d'tor
311     // of PostWorker can happen in a different thread.
312     if (!surfaceGl->bindContext()) {
313         ERR("Failed to bind to post worker context.");
314         return;
315     }
316     mContextBound = true;
317 }
318 
exitImpl()319 void PostWorkerGl::exitImpl() {
320     if (!mContextBound) {
321         return;
322     }
323     s_egl.eglMakeCurrent(s_egl.eglGetDisplay(EGL_DEFAULT_DISPLAY), nullptr, nullptr, nullptr);
324     mContextBound = false;
325 }
326 
327 }  // namespace gfxstream
328