• 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         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, &currentDisplayId,
99                                                     /*x=*/nullptr,
100                                                     /*y=*/nullptr,
101                                                     /*w=*/nullptr,
102                                                     /*h=*/nullptr,
103                                                     /*dpi=*/nullptr,
104                                                     /*flags=*/nullptr,
105                                                     &currentDisplayColorBufferHandle)) {
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, &currentDisplayId, &currentDisplayOffsetX,
132                 &currentDisplayOffsetY, &currentDisplayW, &currentDisplayH,
133                 /*dpi=*/nullptr,
134                 /*flags=*/nullptr, &currentDisplayColorBufferHandle)) {
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