Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 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 | |
| 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 | |
| 51 | PostWorkerGl::PostWorkerGl(bool mainThreadPostingOnly, FrameBuffer* fb, Compositor* compositor, |
Yahan Zhou | 7452555 | 2023-06-20 15:20:42 -0700 | [diff] [blame] | 52 | DisplayGl* displayGl, gl::EmulationGl* emulationGl) |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 53 | : PostWorker(mainThreadPostingOnly, fb, compositor), |
| 54 | m_displayGl(displayGl), |
Yahan Zhou | 7452555 | 2023-06-20 15:20:42 -0700 | [diff] [blame] | 55 | mEmulationGl(emulationGl) { |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 56 | if (!m_displayGl) { |
| 57 | GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "PostWorker missing DisplayGl."; |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | void PostWorkerGl::screenshot(ColorBuffer* cb, int screenwidth, int screenheight, GLenum format, |
| 62 | GLenum type, int skinRotation, void* pixels, Rect rect) { |
Joshua Duong | 3f93b75 | 2023-08-08 10:55:53 -0700 | [diff] [blame] | 63 | // See b/292237104. |
| 64 | mFb->lock(); |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 65 | cb->readToBytesScaled(screenwidth, screenheight, format, type, skinRotation, rect, pixels); |
Joshua Duong | 3f93b75 | 2023-08-08 10:55:53 -0700 | [diff] [blame] | 66 | mFb->unlock(); |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | std::shared_future<void> PostWorkerGl::postImpl(ColorBuffer* cb) { |
Yahan Zhou | 26a9e44 | 2023-05-23 13:15:51 -0700 | [diff] [blame] | 70 | if (!mContextBound || m_mainThreadPostingOnly) { |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 71 | // This might happen on headless mode |
Yahan Zhou | 26a9e44 | 2023-05-23 13:15:51 -0700 | [diff] [blame] | 72 | // Also if posting on main thread, the context binding can get polluted easily, which |
| 73 | // requires frequent rebinds. |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 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(); |
Bo Hu | 0753b08 | 2023-10-24 08:54:07 -0700 | [diff] [blame] | 89 | const bool pixel_fold = multiDisplay.isPixelFold(); |
| 90 | if (pixel_fold) { |
Gurchetan Singh | 80345ad | 2024-04-19 14:22:03 -0700 | [diff] [blame] | 91 | #ifdef CONFIG_AEMU |
Bo Hu | 21366fd | 2024-02-29 11:29:44 -0800 | [diff] [blame] | 92 | if (emugl::shouldSkipDraw()) { |
| 93 | post.layers.clear(); |
| 94 | } else { |
| 95 | post.layers.push_back(postWithOverlay(cb)); |
| 96 | } |
Gurchetan Singh | 80345ad | 2024-04-19 14:22:03 -0700 | [diff] [blame] | 97 | #endif |
Bo Hu | 0753b08 | 2023-10-24 08:54:07 -0700 | [diff] [blame] | 98 | } |
| 99 | else if (multiDisplay.isMultiDisplayEnabled()) { |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 100 | if (multiDisplay.isMultiDisplayWindow()) { |
| 101 | int32_t previousDisplayId = -1; |
| 102 | uint32_t currentDisplayId; |
| 103 | uint32_t currentDisplayColorBufferHandle; |
| 104 | while (multiDisplay.getNextMultiDisplay(previousDisplayId, ¤tDisplayId, |
| 105 | /*x=*/nullptr, |
| 106 | /*y=*/nullptr, |
| 107 | /*w=*/nullptr, |
| 108 | /*h=*/nullptr, |
| 109 | /*dpi=*/nullptr, |
| 110 | /*flags=*/nullptr, |
| 111 | ¤tDisplayColorBufferHandle)) { |
| 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, ¤tDisplayId, ¤tDisplayOffsetX, |
| 138 | ¤tDisplayOffsetY, ¤tDisplayW, ¤tDisplayH, |
| 139 | /*dpi=*/nullptr, |
| 140 | /*flags=*/nullptr, ¤tDisplayColorBufferHandle)) { |
| 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 | |
Bo Hu | 1d90508 | 2023-07-18 16:46:51 -0700 | [diff] [blame] | 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 | } |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 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 | |
| 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. |
| 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. |
| 268 | void PostWorkerGl::clearImpl() { |
Yahan Zhou | 26a9e44 | 2023-05-23 13:15:51 -0700 | [diff] [blame] | 269 | if (!mContextBound || m_mainThreadPostingOnly) { |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 270 | // This might happen on headless mode |
| 271 | setupContext(); |
| 272 | } |
| 273 | m_displayGl->clear(); |
| 274 | } |
| 275 | |
| 276 | std::shared_future<void> PostWorkerGl::composeImpl(const FlatComposeRequest& composeRequest) { |
Yahan Zhou | 26a9e44 | 2023-05-23 13:15:51 -0700 | [diff] [blame] | 277 | if (!mContextBound || m_mainThreadPostingOnly) { |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 278 | // This might happen on headless mode |
| 279 | setupContext(); |
| 280 | } |
| 281 | return PostWorker::composeImpl(composeRequest); |
| 282 | } |
| 283 | |
| 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 { |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 291 | // Create a fake context. |
Yahan Zhou | 7452555 | 2023-06-20 15:20:42 -0700 | [diff] [blame] | 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 | } |
Yahan Zhou | d13d7c8 | 2023-05-15 15:56:55 -0700 | [diff] [blame] | 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 | |
| 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 | |
Bo Hu | 1d90508 | 2023-07-18 16:46:51 -0700 | [diff] [blame] | 327 | } // namespace gfxstream |